Files
bot-bottle/docs/research/pipelock-assessment.md
T
didericis 5e8ca21669
test / unit (pull_request) Successful in 16s
test / integration (pull_request) Successful in 1m32s
docs: replace stale bash-first framing with Python-stdlib-first
The project started life as bash scripts and got rewritten to Python
(documented in docs/research/bash-vs-python-vs-go.md). Several docs
still carried the old "bash-first" framing — misleading for anyone
reading them now (8.7k lines of Python vs. ~130 lines of bash, all
in scripts/demo*.sh).

- CLAUDE.md "What this is" + "Conventions": orchestrator is Python,
  posture is stdlib-first.
- docs/prds/0010-cred-proxy.md, docs/research/manifest-format-and-
  grouping.md: quoted CLAUDE.md's old wording — re-quote.
- docs/research/built-in-supervisor-design.md, landscape-containerized-
  claude.md, agent-sandbox-landscape.md, pipelock-assessment.md,
  network-egress-guard.md: drop "bash-first" claims about the project,
  keep accurate descriptions of external tools' bash usage.

Leaves untouched: bash code-fence syntax in examples, README's
literal `bash scripts/demo.sh` invocation (the demo IS bash),
Claude Code's "Bash tool" references, IVIJL/devbox bash description
(that project actually is bash), and the bash-vs-python-vs-go
research note that records the rewrite decision.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 06:32:42 -04:00

647 lines
36 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Pipelock assessment for claude-bottle egress control
Research into whether pipelock — an open-source AI agent firewall —
is a suitable replacement for, or complement to, the egress-control
approaches documented in `network-egress-guard.md` and the content-
tripwire approach sketched in `secret-exfil-tripwire-encodings.md`.
## Summary
- Pipelock (`luckyPipewrench/pipelock`) is an open-source AI agent
firewall implemented as a single Go binary. It sits inline as an HTTP
forward proxy and, optionally, applies OS-level process containment. It
is the most directly relevant tool found for claude-bottle's egress /
data-exfiltration concerns.
- Its strongest differentiator over the v1 iptables recommendation is
**content-aware DLP**: it matches 48 credential patterns across
multiple encodings in outbound request bodies and URL subdomains,
covering ground that iptables and smokescreen cannot touch.
- The process-containment sandbox (Landlock + seccomp + network
namespaces) is **Linux-only at the kernel level**. In-container on
macOS Docker Desktop the sandbox degrades to `--best-effort` mode,
which relies on `HTTPS_PROXY` env var interception and is bypassable
via raw socket calls or env-var unsetting. The proxy scanning layer
works on both platforms.
- Adopting pipelock as a **sidecar proxy** (replacing the smokescreen v2
recommendation) is well-supported and adds DLP and subdomain-entropy
DNS exfil detection that smokescreen lacks. The sidecar topology avoids
the `--best-effort` degradation problem entirely.
- Recommendation: skip pipelock as an in-container self-contained guard
(the `--best-effort` caveat is significant on macOS Docker Desktop);
adopt it as the **sidecar proxy** in place of smokescreen, pairing it
with the in-container iptables+dnsmasq layer from the v1 plan.
---
## Identification
- **Canonical URL**: <https://github.com/luckyPipewrench/pipelock>
- **Project site / documentation**: <https://pipelab.org/pipelock/>
- **Maintainer**: `luckyPipewrench` (GitHub org); also associated with
the `pipelab.org` domain. No named individual maintainer is surfaced in
search results; the project presents as a company or team product.
- **License**: dual-licensed. Core under **Apache 2.0**; an `enterprise/`
subtree under **Elastic License v2 (ELv2)**, per the badges and `LICENSE`
layout in the project README
([README](https://github.com/luckyPipewrench/pipelock/blob/main/README.md)).
Which capabilities sit on which side of that line is a question this note
does not answer; before adoption, the `enterprise/` directory should be
inspected to confirm that the features cited below (DLP patterns,
subdomain entropy, MCP scanning, request redaction, sidecar topology) are
available under the Apache 2.0 core. Mediator-signed action receipts and
some attestation features may be ELv2-gated.
- **Current stable release**: v2.3.0, published **2026-04-25** per the
GitHub release metadata
([release v2.3.0](https://github.com/luckyPipewrench/pipelock/releases/tag/v2.3.0)).
An accompanying Help Net Security announcement appeared 2026-05-04
([helpnetsecurity.com](https://www.helpnetsecurity.com/2026/05/04/pipelock-open-source-ai-agent-firewall/));
earlier rough drafts of this note conflated the announcement date with
the release date.
- **Project age**: repository created **2026-02-08** per the GitHub API,
so ~3 months old at the time of this note. v2.1.1, v2.1.2, v2.2.0, v2.3.0
tags exist, indicating active iteration.
- **Language / distribution**: Go; ships as a single static binary (~1220 MB,
sources differ slightly). Available via Docker image
(`ghcr.io/luckypipewrench/pipelock:latest`), Homebrew
(`brew install luckyPipewrench/tap/pipelock`), and
`go install`. Stated zero runtime dependencies.
**What it actually does.** Pipelock is an application-layer HTTP forward
proxy designed specifically for AI agent deployments. It sits between the
agent process and the network and applies an 11-layer scanning pipeline to
all outbound HTTP, HTTPS (via CONNECT), WebSocket, and MCP traffic. The
pipeline covers: scheme enforcement, CRLF injection detection, path traversal
blocking, domain blocklisting, DLP credential matching, subdomain entropy
analysis, SSRF protection (RFC 1918 + metadata endpoints blocked by default),
rate limiting, URL length limits, and per-domain data budgets. An optional
sandbox mode on Linux wraps the agent child process with Landlock filesystem
restrictions, seccomp syscall filtering, and a network namespace that forces
all traffic through pipelock's scanner. On macOS the equivalent is
`sandbox-exec` with dynamic SBPL profiles. The tool is positioned at the
intersection of egress control and AI-specific threat vectors (prompt
injection, MCP tool poisoning, credential leaks in LLM outputs).
**Disambiguation note.** The name "pipelock" could in principle refer to
Python pip lockfile tooling or other unrelated utilities. In the context of
containerized AI agent egress filtering there is exactly one relevant project:
`luckyPipewrench/pipelock`. No other tool bearing that name addresses the
egress / DLP problem space.
---
## Capabilities vs the threat model
### HTTP(S) egress
Pipelock covers HTTP and HTTPS (CONNECT) egress in full. Its forward proxy
mode requires zero code changes in the agent — Claude Code already respects
`HTTP_PROXY` / `HTTPS_PROXY` environment variables, which is the standard
injection vector
([Claude Code network config docs](https://code.claude.com/docs/en/network-config)).
In `strict` mode, all outbound HTTP is blocked unless the destination is on
the `api_allowlist`; in `balanced` and `audit` modes the allowlist is advisory.
This covers threat 1 (HTTP(S) data exfiltration) from the `network-egress-guard.md`
threat model.
The proxy scanning pipeline also covers outbound WebSocket and MCP traffic,
which the iptables approach allows through on the basis of IP address alone
and which smokescreen passes without content inspection. Pipelock's
bidirectional MCP scanner catches credential leaks in MCP tool call arguments
and prompt-injection payloads in tool responses, neither of which an IP- or
CONNECT-hostname-based firewall can detect.
What it leaves uncovered at this layer: raw TCP (non-HTTP), UDP, and ICMP.
These are the same gaps documented for smokescreen in `network-egress-guard.md`
and must be closed by a complementary iptables or `--network none` layer.
### DNS exfiltration
This is pipelock's most significant advantage over the v1 iptables
recommendation and over smokescreen. The `network-egress-guard.md` note
flags DNS tunneling (threat 2) as a real gap in IP-based allowlists and
notes that the only partial mitigations are a controlled resolver (dnsmasq)
plus blocking direct UDP/53 access to non-allowlisted resolvers.
Pipelock addresses the same problem from a different angle: it runs a
**subdomain entropy analysis** pass against every URL before resolving it.
This catches base64- and hex-encoded keys embedded in subdomain labels —
the dominant DNS exfiltration technique — by detecting abnormally high
Shannon entropy in the leftmost subdomain components. Crucially, the DLP
scanner runs **before** DNS resolution, meaning a secret encoded into a
subdomain cannot escape via the DNS query itself even if the HTTP request
is later blocked
([pipelab.org/pipelock/](https://pipelab.org/pipelock/)).
A DEV Community post by the maintainer explicitly addresses the
"AI agents leak API keys through DNS queries" scenario
([dev.to/luckypipewrench/your-ai-agent-leaks-api-keys-through-dns-queries-5c1d](https://dev.to/luckypipewrench/your-ai-agent-leaks-api-keys-through-dns-queries-5c1d)).
Coverage gaps remain. Pipelock operates at the HTTP proxy layer; it does
not intercept raw UDP port 53 traffic from the container. An agent making a
direct DNS query (not mediated through pipelock's resolver) can still
exfiltrate via DNS. Closing this gap still requires one of:
- Blocking direct UDP/53 from the container via iptables and routing all
DNS through a controlled resolver, or
- Combining pipelock's subdomain-entropy check (effective at the URL layer)
with an in-container dnsmasq that blocks non-allowlisted queries.
The dnsmasq complement from the v1 plan therefore remains relevant even if
pipelock is adopted as the sidecar proxy.
### RFC 1918 / lateral movement
Pipelock blocks connections to private IP ranges, cloud metadata endpoints
(169.254.169.254), and link-local addresses as part of its SSRF protection
layer, which is active by default
([pipelock/docs/configuration.md](https://github.com/luckyPipewrench/pipelock/blob/main/docs/configuration.md)).
This matches the behavior of smokescreen, which also blocks RFC 1918 by
default and was cited in `network-egress-guard.md` as a primary advantage
of the proxy sidecar approach over raw iptables rules.
Pipelock adds DNS rebinding protection on top, which smokescreen does not
document: it checks that the IP a hostname resolves to at connection time
is not in a private range, guarding against the class of attack where a
benign domain is resolved to a public IP during the allowlist check and then
re-resolved to a private IP when the connection is made.
Domains can be whitelisted from the SSRF IP check individually (for
legitimate VPN-routed internal APIs), while the DLP scanner continues to
apply to those connections
([pipelab.org/pipelock/](https://pipelab.org/pipelock/)).
### Content-based exfil detection (DLP)
This is the area where pipelock most directly covers ground not addressed
by either the v1 iptables approach or the smokescreen sidecar, and it maps
directly to the problem space of `secret-exfil-tripwire-encodings.md`.
The DLP layer matches **48 credential patterns** across the URL, headers, and
request body. It uses four checksum validators (Luhn, mod-97, ABA routing
numbers, WIF bitcoin keys) to reduce false positives and applies
encoding-aware decoding before matching: at minimum base64, hex, multi-layer
URL encoding, and Unicode evasion (confusable characters, combining marks,
control character insertion, field splitting, whitespace manipulation)
([pipelab.org/pipelock/](https://pipelab.org/pipelock/),
[github.com/luckyPipewrench/pipelock/blob/main/docs/security-assurance.md](https://github.com/luckyPipewrench/pipelock/blob/main/docs/security-assurance.md)).
The subdomain entropy check covers environment variable leaks (values 16+
characters with Shannon entropy above 3.0) in base64, hex, and base32 forms.
As of v2.3.0, the DLP layer also covers **request redaction**: matched
secrets are rewritten in-flight with typed placeholders
(`<pl:aws-access-key:1>`, etc.) rather than simply blocked, preserving the
class information for downstream tooling while stripping the value from the
outbound request
([pipelab.org/blog/pipelock-v230-release/](https://pipelab.org/blog/pipelock-v230-release/)).
This addresses the use case described in `secret-exfil-tripwire-encodings.md`
where the goal is detection rather than total blocking.
Coverage relative to the encoding catalog in `secret-exfil-tripwire-encodings.md`:
the 25-encoding table in that note ranges from base64 (covered) through
ROT13, double base64, and gzip+base64. Pipelock's documented encoding
coverage (base64, hex, URL encoding, Unicode normalization) covers
approximately the first ten entries of that catalog. There is no documented
coverage for ROT13, ROT47, gzip+base64, or steganographic encodings — consistent
with that note's conclusion that those are diminishing-returns cases for a
non-adversarially-sophisticated agent.
The encoding-normalization pipeline applies before the pattern match, so
a secret base64-encoded inside a JSON body will be decoded and matched; it
is not necessary to pre-compute all encoded forms and load them as separate
patterns, which was the DIY approach sketched in `secret-exfil-tripwire-encodings.md`.
### Scope gaps
The following threat-model items from `network-egress-guard.md` are
**not** addressed by pipelock at the proxy layer:
- **Raw TCP (non-HTTP)**: direct database connections, LDAP, custom
protocols, and anything that does not go through `HTTPS_PROXY`. These
require a complementary IP-based default-deny layer (iptables or
`--network none`).
- **UDP**: DNS queries bypassing the proxy, QUIC, ICMP covert channels.
Pipelock's subdomain entropy check fires on URLs it sees; it does not
intercept raw UDP 53 packets.
- **Domain fronting**: an agent can send `CONNECT allowed-host.com:443`
through the proxy but embed a different SNI inside the TLS session.
Pipelock supports TLS interception via its `tls_interception` config
block (`enabled`, `ca_cert`, `ca_key`, `cert_ttl`, `cert_cache_size`,
`passthrough_domains`, `max_response_bytes`) plus the `pipelock tls
init` / `install-ca` / `show-ca` CLI; with interception on, the
body and inner Host header become visible to its scanner pipeline,
closing the domain-fronting gap. With interception off (default in
the generated config), pipelock relays the CONNECT as an opaque
tunnel and only sees the outer hostname.
- **SSH egress content**: SSH sessions to permitted hosts are opaque.
Same limitation noted in both prior research notes.
- **Agent killing the proxy process**: if pipelock runs inside the same
container as the agent rather than as a sidecar, the agent's shell
access could kill the proxy process. The sidecar topology mitigates this.
---
## Fit for claude-bottle
### Deployment topology
Pipelock explicitly supports two deployment shapes relevant to
claude-bottle:
**Sidecar proxy.** A separate container running pipelock on an
`--internal` Docker network, with the agent container's only route to the
outside world through the proxy. Pipelock can generate a ready-made Docker
Compose file for this topology:
```
pipelock generate docker-compose --agent claude-code -o docker-compose.yaml
```
The agent container is placed on a network with `internal: true` (which
removes the default gateway at the iptables level inside Docker's network
plumbing); the only reachable address is the pipelock container. The agent
sets `HTTPS_PROXY` / `HTTP_PROXY` to the pipelock service address. This is
structurally identical to the smokescreen sidecar described in
`network-egress-guard.md`, but with the richer scanning pipeline. The
Docker image is available at `ghcr.io/luckypipewrench/pipelock:latest`
([pipelock/docs/guides/deployment-recipes.md](https://github.com/luckyPipewrench/pipelock/blob/main/docs/guides/deployment-recipes.md)).
**In-container with sandbox enabled.** Pipelock forks the agent as a child
process, wraps it with OS-level containment, and routes all traffic through
its own scanner. This avoids a second container but introduces the
`--best-effort` degradation problem described below and means the agent and
the proxy run in the same failure domain.
The sidecar topology is recommended for claude-bottle because it matches
the existing Python-orchestrated multi-container model (the SSH key agent
already uses a separate process), keeps pipelock outside the agent's kill
reach, and avoids the `--best-effort` issue on macOS Docker Desktop.
The claude-bottle manifest model would need one new piece of plumbing: a
per-agent pipelock ACL YAML generated from the manifest's `allowlist`
and `ssh` entries, analogous to what the smokescreen section of
`network-egress-guard.md` already sketches. The `cli.py` changes required
are the same pattern: `docker network create --internal`, `docker run` for
the proxy container, then `docker run` for the agent with `HTTPS_PROXY`
injected.
### macOS Docker Desktop compatibility
The proxy layer of pipelock (the HTTP forward proxy, DLP scanning, SSRF
blocking, subdomain entropy analysis) works on both macOS and Linux. The
Docker image and Homebrew package are available for macOS. There are no
Linux-specific kernel dependencies for the proxy functionality itself.
The **sandbox mode** is where the platform split matters. On Linux the
sandbox uses Landlock LSM, seccomp, and network namespaces, all requiring
Linux 5.13+ kernel features. On macOS it uses `sandbox-exec` with SBPL
profiles, which requires macOS 13+. When pipelock runs **inside** a Docker
container on macOS Docker Desktop, the container's kernel is Linux (LinuxKit),
so the sandbox mode attempts to use the Linux path. Whether the LinuxKit
kernel version in Docker Desktop ships with the necessary Landlock ABI v1
support (Linux 5.13+) and allows `CLONE_NEWUSER` for unprivileged network
namespaces is a version-dependent question not authoritatively answered in
the pipelock documentation.
Pipelock itself documents that if `CLONE_NEWUSER` is unavailable inside a
container, it falls back to `--best-effort` mode:
> When user namespaces are unavailable, pipelock cannot create a network
> namespace for the child process, so outbound traffic enforcement relies
> only on HTTP_PROXY / HTTPS_PROXY environment variables, which can be
> bypassed.
> ([github.com/luckyPipewrench/pipelock](https://github.com/luckyPipewrench/pipelock))
In the **sidecar topology** this problem disappears: pipelock runs as its
own container with no child-process sandbox, and enforcement is achieved via
Docker's `--internal` network flag (which removes the default route at the
iptables level in the LinuxKit VM). The agent cannot bypass this by unsetting
`HTTPS_PROXY` because there is literally no route to the internet from its
network namespace. The sidecar is therefore the correct deployment shape for
macOS Docker Desktop.
### Sandbox mode in-container caveat
Even on a Linux Docker Engine host, running pipelock in sandbox mode inside
a container requires careful capability configuration. The sandbox uses
`CLONE_NEWUSER` to create an unprivileged network namespace; Docker's default
seccomp profile may block this syscall. The fix is either to adjust the
container's seccomp profile, set `kernel.unprivileged_userns_clone=1` on the
host, or use the sidecar topology instead. In `--best-effort` mode pipelock
emits a startup `WARNING: DEGRADED` log line and relies only on
proxy-environment-variable interception — meaning a subprocess that calls
`unsetenv("HTTPS_PROXY")` before making a socket call bypasses the scanner.
The pipelock documentation explicitly acknowledges this bypass and recommends
the sidecar topology as the kernel-enforced equivalent for containerized
deployments
([github.com/luckyPipewrench/pipelock](https://github.com/luckyPipewrench/pipelock)).
### Configuration shape and manifest integration
Pipelock is configured via a single YAML file. A starter config can be
generated with `pipelock generate config --preset balanced > pipelock.yaml`.
The config watcher picks up changes at runtime (100ms debounce on SIGHUP or
file events), so per-agent ACL updates do not require a container restart.
For claude-bottle, the relevant per-agent configuration is the domain
allowlist. The manifest already captures the necessary inputs: the `ssh`
array has target hostnames, and an `allowlist` key is planned for the v2
egress work (per `network-egress-guard.md`). Generating a per-agent pipelock
YAML from those manifest fields is the same pattern as the per-agent
smokescreen ACL YAML described in that note — a straightforward `lib/` script
producing a temp-file YAML and `docker cp`'ing it into the proxy container.
The YAML format is more expressive than smokescreen's YAML ACL: it also
carries DLP sensitivity settings, per-domain data budgets, and rate limits.
For a first integration pass, only the `api_allowlist` section needs
per-agent population; the rest of the defaults are appropriate for the
claude-bottle threat model.
### Runtime footprint
A single Go binary, ~1220 MB (sources report slightly different figures; the
GitHub description says "~20 MB" and the randomcpu.com writeup says "~12 MB").
Zero runtime dependencies; the Go standard library is statically linked. This
is consistent with claude-bottle's low-dependency principle. Adding Go as a
host build dependency is not required — the binary is fetched from a Docker
image or Homebrew.
The Docker image at `ghcr.io/luckypipewrench/pipelock:latest` is the natural
integration point for the sidecar topology. No new runtimes or package managers
are needed on the host; the manifest-and-launch scripts stay in bash.
One new concept is the pipelock YAML config file format. It is documented and
generated by `pipelock generate config`; the schema is YAML with clear keys. It
is simpler to reason about than iptables rules and does not require per-container
privilege grants.
---
## Comparison table
The axes match those used in `network-egress-guard.md` with three additions
(DLP/content detection, DNS subdomain entropy, project recency) to capture
pipelock's differentiators.
| | iptables+dnsmasq (v1) | smokescreen sidecar (v2) | pipelock sidecar | do nothing |
|---|---|---|---|---|
| HTTP(S) egress blocking | hostname-resolved IPs only; CDN over-permissioning risk | hostname (CONNECT) | hostname (CONNECT) + 11-layer scan | none |
| DNS exfil — raw UDP | blocked (dnsmasq + iptables) | not blocked | not blocked (proxy layer only) | not blocked |
| DNS subdomain entropy detection | no | no | yes, pre-resolution | no |
| Blocks RFC 1918 by default | only if explicitly added to rules | yes | yes, + DNS rebinding | no |
| Content-based DLP (credential patterns) | no | no | yes, 48 patterns + encoding normalization | no |
| MCP / WebSocket scanning | no | no | yes, bidirectional | no |
| Domain fronting bypass | possible | possible | mitigated when `tls_interception` is enabled (CA trust required in client) | n/a |
| macOS Docker Desktop (sidecar mode) | yes | yes | yes | yes |
| macOS Docker Desktop (in-container sandbox) | yes | n/a | degraded (--best-effort) | yes |
| NET_ADMIN / NET_RAW required | yes | no | no (sidecar) | no |
| New runtime on host | no | Go image (docker pull) | Go image (docker pull) | no |
| Config format | bash script + iptables rules | YAML ACL | YAML (generated by CLI) | n/a |
| Implementation effort | lowmedium (adapt existing script) | medium | medium (similar to smokescreen) | none |
| Project maturity (as of 2026-05-08) | battle-tested (Linux kernel + Anthropic devcontainer) | battle-tested (Stripe production) | v2.3.0 (2026-04-25), repo ~3mo old, Apache 2.0 core + ELv2 enterprise | n/a |
---
## Recommendation
**Adopt pipelock as the v2 sidecar proxy in place of smokescreen.**
The reasoning:
1. **It replaces smokescreen with a strict superset.** Pipelock covers
everything smokescreen covers (CONNECT-based hostname allowlisting,
RFC 1918 blocking, Docker `--internal` network isolation) and adds DLP,
subdomain-entropy DNS exfil detection, MCP scanning, and request
redaction. The integration shape for claude-bottle is identical: a
separate container on an internal Docker network, with the agent's
`HTTPS_PROXY` pointing at it. The `cli.py` changes are the same pattern.
2. **The DLP layer is the most direct answer to the content-tripwire gap.**
The `secret-exfil-tripwire-encodings.md` note concluded that no
off-the-shelf tool did exactly what was needed and sketched a ~200-line
Python DIY. Pipelock's 48-pattern, encoding-aware DLP pipeline with
built-in false-positive reduction is a production-ready answer to that
gap, without writing custom code.
3. **The `--best-effort` problem does not apply to the sidecar topology.**
The concern about Linux namespace availability inside Docker containers
is real, but irrelevant when pipelock runs as a sidecar in its own
container. The network isolation is enforced by Docker's `--internal`
flag at the LinuxKit level, not by pipelock's in-process sandbox.
4. **The v1 iptables layer should be kept.** Pipelock covers HTTP/HTTPS
only; raw TCP, UDP, and ICMP still need the iptables default-deny.
The existing v1 plan (iptables + dnsmasq for DNS + `NET_ADMIN`) remains
necessary as the IP-level floor. Pipelock sits above it, not instead of it.
The dnsmasq layer also closes the raw-UDP-53 gap that pipelock's proxy
layer cannot reach.
5. **The project is young.** v2.3.0 shipped on 2026-04-25, the repository
was created 2026-02-08 — roughly three months of public history at the
time of this note. The `luckyPipewrench` org does not appear to be a
well-known security vendor with a published track record. This is a
legitimate reason for caution. Smokescreen has years of production use at
Stripe. Pipelock should be evaluated against its code (Apache 2.0 core,
ELv2 enterprise subtree) before being trusted at the enforcement boundary.
The appropriate response is to keep the iptables layer as the primary
enforcement mechanism and treat pipelock as an additional detection and
content-inspection layer, not the sole barrier.
**Revised tiering:**
- **v1 (unchanged):** in-container iptables + ipset + dnsmasq, as specified
in `network-egress-guard.md`. This closes the IP, TCP, UDP, and raw DNS
attack surface.
- **v2 (updated):** pipelock sidecar instead of smokescreen sidecar. Adds
hostname-based allowlisting, content DLP, subdomain entropy analysis, and
MCP scanning on top of the v1 IP layer. Implementation effort is comparable
to the smokescreen plan; capabilities are a strict superset for the
claude-bottle threat model.
- **DIY tripwire script (deferred):** the `secret-exfil-tripwire-encodings.md`
DIY sketch can be deferred entirely if pipelock's DLP patterns cover the
secrets in use. Custom patterns (for secrets not matching pipelock's 48
built-ins) can be added via pipelock's signed rule bundle mechanism when
and if needed.
---
## Does pipelock make claude-bottle redundant?
Pipelock is itself an AI-agent firewall with an in-process sandbox mode,
which raises a fair question: if pipelock can already wrap an agent process
with Landlock + seccomp + namespaces (or `sandbox-exec` on macOS), is the
Docker-container layer that claude-bottle provides still doing useful work?
The short answer: **no, pipelock does not make claude-bottle redundant**.
The two operate at different layers and the overlap is narrow.
### Where pipelock substitutes for parts of claude-bottle
For a single-agent use case on Linux with full unprivileged-userns support,
`pipelock sandbox -- claude` could replace the Docker container with a
process-level sandbox: Landlock restricts the filesystem, seccomp filters
syscalls, the network namespace forces traffic through the scanner. That is
a real isolation primitive, not a fig leaf. A user whose only concern is
"don't let one agent's bug touch my home directory or exfil my keys" could
plausibly run pipelock on the host and skip Docker entirely.
### Where claude-bottle does work pipelock does not
The redundancy argument breaks down once the actual goals from
`CLAUDE.md` are enumerated:
1. **Filesystem isolation that survives a misbehaving agent.** Docker
containers give an entire kernel-mediated mount namespace and a separate
root filesystem. Landlock restricts a process's filesystem view but the
process still runs in the host's mount namespace, with the host's
`/etc`, `/proc`, `/dev`, and so on visible (modulo the Landlock ruleset).
For "Claude with `--dangerously-skip-permissions`", a fresh per-agent
filesystem with nothing in `$HOME` except what was deliberately copied
in is materially stronger than a Landlock-restricted view of the host
filesystem.
2. **Parallel agents.** A primary stated goal is "Allow me to easily spin
up agent tasks in parallel". claude-bottle launches one container per
agent invocation with a slug-derived name and a numeric suffix on
conflict. Pipelock has no equivalent fleet-management concept; it is a
per-process wrapper. Running `pipelock sandbox -- claude` four times in
parallel on the host gives four sandboxed Claude processes sharing the
same `$HOME`, the same shell history, the same `~/.claude/`, the same
keychain. That is not the same property as four containers each with
its own ephemeral filesystem.
3. **The manifest model.** claude-bottle's `claude-bottle.json` carries
per-agent `env`, `skills`, `prompt`, and `ssh` configuration with
precise resolution semantics (prompt-at-launch secrets, host-env
forwarding, literal env-file values, host-key fingerprint pinning).
Pipelock has a YAML config for its own scanning rules but no concept of
"for agent X use these env vars, skills, system prompt, and SSH keys".
These are different problem domains.
4. **Ephemeral state by construction.** Every container start is a fresh
filesystem from the image; nothing the agent writes persists unless the
user explicitly mounts a volume. This property is not derivable from
pipelock — a pipelock-sandboxed agent on the host writes to the host's
filesystem (subject to the Landlock ruleset), and persists across
invocations.
5. **The SSH-agent-in-container design.** The agent socket runs as root
inside the container, the keys are deleted from disk after loading,
and `socat` proxies node's connections so the OpenSSH peer-credential
check passes. This solves a specific Docker-Desktop-on-macOS limitation
(no UDS forwarding from host agent into the VM) and gives the property
that the `node` user can sign with the key but cannot read its bytes.
Pipelock does not address SSH at all, which is one of its documented
gaps. claude-bottle's solution remains relevant under either deployment.
6. **Skill-directory injection per agent.** The `skills` array copies named
directories from `~/.claude/skills/` into the container at launch. There
is no analogous concept in pipelock; the skill set claude-bottle exposes
is part of the per-agent isolation model, not just a configuration.
7. **Shareability of agent definitions.** A `claude-bottle.json` file can
be checked into a project repo, and a third party can run the same
agent with the same env-resolution rules. Pipelock configurations are
per-installation; they do not encode "this is an agent named X".
### The opposite question
Does claude-bottle make pipelock redundant? Equally no. Docker container
isolation does nothing about content-level exfil over an allowed channel.
A misbehaving agent inside a claude-bottle container with HTTPS access to
`api.anthropic.com` can still attempt to exfiltrate via DNS subdomain
encoding, prompt-injection responses from MCP servers, or covert HTTP
parameters. Those are exactly the threats pipelock is designed to detect.
The containment argument for claude-bottle and the content-inspection
argument for pipelock do not overlap.
### Net conclusion
Pipelock and claude-bottle are layered defenses, not alternatives.
claude-bottle provides filesystem isolation, per-agent state ephemerality,
fleet management, manifest-driven configuration, and the SSH-agent-without-
key-leak property. Pipelock provides hostname allowlisting, content-aware
DLP, MCP scanning, and subdomain-entropy DNS exfil detection at the network
boundary. The strongest deployment is both: pipelock as a sidecar on the
container's only egress route, claude-bottle as the per-agent container
orchestrator. Removing either layer leaves a real and named threat
uncovered.
The one scenario in which adopting pipelock could justify retiring
claude-bottle is a single-user, single-agent, host-resident deployment
where the user is willing to give up the parallel-agent goal, accept
Landlock-level filesystem restriction in place of mount-namespace
isolation, and re-implement env / skill / SSH-key / prompt management
some other way. That is not the use case the project was built for.
---
## Open questions
1. **Who is `luckyPipewrench`?** The maintainer is not publicly identified
as an individual or a named organization with a security track record.
Before relying on pipelock at a detection boundary, a code review of the
DLP matching and scanning pipeline is warranted. The repo is public under
Apache 2.0 at <https://github.com/luckyPipewrench/pipelock>.
2. **Does the pipelock Docker image introduce supply-chain risk?**
Pulling `ghcr.io/luckypipewrench/pipelock:latest` brings in a binary
from an unvetted source. Pinning by digest (as the CLAUDE.md recommends
for supply-chain hygiene) and building from source are both options.
3. **What is the actual DLP false-positive rate for the secrets claude-bottle
agents use?** The 48 patterns cover well-known credential formats. Custom
patterns can be added but the mechanism (signed rule bundles) is not
documented in detail in public search results. Before v2, testing against
real agent traffic is needed to establish the baseline FP rate.
4. **Does the dnsmasq layer remain necessary alongside pipelock sidecar?**
Yes, for raw UDP/53 interception. Pipelock's subdomain entropy check fires
on URLs it processes; it does not intercept DNS packets that bypass the
proxy. The controlled resolver closes that gap. Re-evaluate if pipelock
adds a DNS interception mode in a future release.
5. **Which features are Apache 2.0 vs ELv2-only?** The repo carries an
`enterprise/` subtree under the Elastic License v2. Several features
discussed in this note (mediator-signed action receipts, signed rule
bundles, possibly some scanning passes) may live there. Before scoping
integration work, audit which capabilities cited above are in the
Apache 2.0 core and which require accepting ELv2 terms (which permit
internal use and modification but prohibit offering pipelock as a
managed service). For claude-bottle's local-Docker single-user use case,
ELv2 is likely acceptable, but the determination should be explicit.
6. **Can pipelock's YAML config be generated per-agent from the manifest in
a way that handles the `ssh` array correctly?** The `ssh` array in
`claude-bottle.json` contains hostnames, ports, and `KnownHostKey` entries.
These need to be mapped to pipelock's `api_allowlist` (for HTTP) and
potentially to a separate bypass for the SSH socket. SSH is opaque to the
HTTP proxy and does not go through `HTTPS_PROXY`; the allowlist entry is
relevant only if the SSH host is also reached over HTTPS for key exchange
or API calls.
---
## References
- `luckyPipewrench/pipelock` GitHub: <https://github.com/luckyPipewrench/pipelock>
- Pipelock project site: <https://pipelab.org/pipelock/>
- Pipelock v2.3.0 release post: <https://pipelab.org/blog/pipelock-v230-release/>
- Pipelock v2.3 upgrade guide: <https://pipelab.org/learn/pipelock-v230-upgrade/>
- Pipelock v2.2.0 release (newreleases.io): <https://newreleases.io/project/github/luckyPipewrench/pipelock/release/v2.2.0>
- Help Net Security announcement (2026-05-04): <https://www.helpnetsecurity.com/2026/05/04/pipelock-open-source-ai-agent-firewall/>
- Pipelock configuration reference: <https://github.com/luckyPipewrench/pipelock/blob/main/docs/configuration.md>
- Pipelock security assurance doc: <https://github.com/luckyPipewrench/pipelock/blob/main/docs/security-assurance.md>
- Pipelock deployment recipes: <https://github.com/luckyPipewrench/pipelock/blob/main/docs/guides/deployment-recipes.md>
- Pipelock agent firewall explainer: <https://pipelab.org/agent-firewall/>
- Cloudflare Sandboxes + Pipelock two-layer egress: <https://pipelab.org/learn/cloudflare-sandboxes-pipelock/>
- Pipelock vs Docker MCP Gateway comparison: <https://pipelab.org/compare/pipelock-vs-docker-mcp-gateway/>
- DEV Community — AI agents leak API keys through DNS queries: <https://dev.to/luckypipewrench/your-ai-agent-leaks-api-keys-through-dns-queries-5c1d>
- DeepWiki entry: <https://deepwiki.com/luckyPipewrench/pipelock>
- randomcpu.com writeup: <https://randomcpu.com/pipelock-agent-firewall-for-ai-coding-tools/>
- `stripe/smokescreen` (v2 baseline): <https://github.com/stripe/smokescreen>
- Anthropic `init-firewall.sh` (v1 baseline): <https://github.com/anthropics/claude-code/blob/main/.devcontainer/init-firewall.sh>
- Claude Code network config docs: <https://code.claude.com/docs/en/network-config>
- Prior research — network egress guard: `docs/research/network-egress-guard.md`
- Prior research — secret exfil tripwire encodings: `docs/research/secret-exfil-tripwire-encodings.md`
Research conducted 2026-05-08.