Posted on September 12, 2025
Default‑deny HTTP(S) for dev tools and AI agents. Script rules in JS or shell, log every request, and keep egress within your policy.
Coding agents are becoming more powerful every day without commensurate security
and governance tooling. The result is a world where solo developers happily run claude --dangerously-skip-permissions for hours unmoderated while many of the world's most important organizations have barely tried agentic developmentLearned from our experience at Coder . I've been working on a tool called httpjail in an effort to make agents available everywhere.
The tool is focused on mitigating these classes of risks:
| Risk | Example |
|---|---|
| Agents performing destructive actions | Deleting your database |
| Agents leaking sensitive information | Exposing API keys or credentials |
| Agents operating with more authority than desired | Pushing straight to main instead of opening a PR |
Agents may transgress accidentally (user misinterpretation) or intentionally (prompt injection).
There is a class of risks at the file-system interface too, but, I believe existing tooling (containers) is sufficient here. Existing network isolation tools rely on IP-based rules. In our case, they're imprecisecentralized, anycast load balancers power much of the internet and require constant maintenanceIPs change randomly and they're not a part of a service's implicit "contract".
httpjail
httpjail implements an HTTP(S) interceptor alongside process-level network isolation. Under default configuration, all DNS (udp:53) is permitted and
all other non-HTTP(S) traffic is blocked.
httpjail rules are either JavaScript expressions or custom programs. This approach makes
them far more flexible than traditional rule-oriented firewalls and avoids the learning curve of a DSL.
Block all HTTP requests other than the LLM API traffic itself:
$ httpjail --js "r.host === 'api.anthropic.com'" -- claude "build something great"
Allow only GET requests i.e. make the internet read-only:
$ httpjail --js "r.method === 'GET'" -- claude "build something great"
Only allow hosts in a whitelist.txt file:
$ httpjail --sh "grep -qx \"$HTTPJAIL_HOST\" whitelist.txt" -- claude "research these APIs"
How it works
In a nutshell:
%%{init: {'theme':'dark', 'themeVariables': {'fontSize': '24px'}}}%%
graph LR
subgraph "1. Setup"
direction TB
Start{Mode?}
Start -->|Linux
Strong| NS[Create namespace
+ nftables redirect
+ setuid $SUDO_USER]
Start -->|macOS/--weak
Weak| Env[Set $HTTP_PROXY
env vars]
end
subgraph "2. Target Process"
direction TB
Target[Target Process
e.g., claude]
Target --> Request[HTTP/HTTPS
Request]
Request --> Route{Route
Request}
end
subgraph "3. Interception"
direction TB
Proxy[Proxy :8080/:8443]
Proxy --> Rules{Evaluate
JS/Script Rules}
end
subgraph "4. Result"
direction TB
Internet[✓ Internet Access]
Blocked[✗ 403 Blocked]
Bypass[⚠️ BYPASSED!
weak mode only]
end
NS --> Target
Env --> Target
Route -->|Strong: forced
via nftables| Proxy
Route -->|Weak: respects
$HTTP_PROXY| Proxy
Route -->|Weak: ignores
$HTTP_PROXY| Bypass
Rules -->|Allow| Internet
Rules -->|Deny| Blocked
style NS fill:#00d4ff,color:#0a0a0a
style Env fill:#00d4ff,color:#0a0a0a
style Proxy fill:#404040
style Blocked fill:#8b2635
style Internet fill:#2a7f62
style Bypass fill:#8b2635
macOS (Weak Mode)
macOS uses --weak/-w mode by default (see #7).
In weak mode, we rely on process cooperation via the standard HTTP_PROXY/HTTPS_PROXY environment variables. This mode is less of a jail and more of a suggestion that the majority of
well-meaning applications happen to comply with.
TLS Interception
httpjail implements full TLS interception to inspect and filter HTTPS traffic. Without interception, rules would only have access to the hostname via SNI, and most of the power of this tool would be lost.
How TLS Interception Works
Certificate Authority Generation: On first run,
httpjailgenerates a self-signed Certificate Authority (CA) that's stored in~/.config/httpjail/. This CA is used to sign certificates for intercepted connections.Dynamic Certificate Generation: When a client connects,
httpjail:- Extracts the Server Name Indication (SNI) from the TLS ClientHello
- Generates a certificate on-the-fly for that specific hostname
- Uses a shared ECDSA P-256 key pair for all server certificates for O(1) keygen overhead
- Signs it with the CA certificate
- Caches certificate in memory for performance
Dual Mode Operation:
- Transparent Proxy Mode: Directly accepts TLS connections by detecting the TLS ClientHello packet (starts with
0x16) - Explicit Proxy Mode: Handles HTTP CONNECT tunnels, responds with "200 Connection Established", then transitions to Transparent Proxy Mode
- Transparent Proxy Mode: Directly accepts TLS connections by detecting the TLS ClientHello packet (starts with
Trust Injection: The CA certificate must be trusted by client applications.
httpjailautomatically sets environment variables for common tools:SSL_CERT_FILE/SSL_CERT_DIRfor OpenSSL-based toolsCURL_CA_BUNDLEfor curlNODE_EXTRA_CA_CERTSfor Node.jsREQUESTS_CA_BUNDLEfor Python requestsGIT_SSL_CAINFOfor Git
Jail Escapes
In the weak jail, it's trivial for the agent to escape the jail by funnelling requests through a
shim program that disregards the HTTP_PROXY environment variable.
Even the strong jail is not perfect. There are potential escape hatches in the filesystem. For example, the agent could create a container via a Docker socket which would spawn outside the network namespace.
To combine filesystem and network isolation into one, httpjail has a --docker-run flag that works like this:
httpjail --js "r.host === 'api.github.com'" --docker-run -- \
--rm alpine:latest wget -qO- https://api.github.com
I believe there's still much value in this approach even with imperfect isolation. In my experience, models seldom try to escape restrictions intentionally placed by the user. And, if the jail is rendered ineffective by prompt injection, it wasn't doing its job in the first place.
Server Mode
For the strongest level of isolation, you can:
- Run
httpjail --serveron a standalone server. - Configure the network firewall to only permit 80/443 traffic to the proxy server.
- Optionally, you may redirect all traffic to the proxy server, otherwise you will need to take care in ensuring HTTP_PROXY is set in your environments and all of your web-faring applications respect it.
- Run processes as usual in the development environment.
%%{init: {'theme':'dark', 'themeVariables': {'fontSize': '24px'}}}%%
graph LR
subgraph "Dev Environment"
Request[HTTP/HTTPS
Request]
Check{Respects
$HTTP_PROXY?}
end
subgraph "Network Layer"
FW[Network Firewall]
Config{Config
Mode?}
FW --> Config
Config -->|Redirect All
80/443 Traffic| Force[Forced to Proxy]
Config -->|Allow Only
Proxy IP| Check
Check -->|Yes| Voluntary[To Proxy]
Check -->|No| Drop[✗ Dropped]
end
subgraph "httpjail --server"
Decision{Evaluate
Rules}
end
subgraph Result
direction TB
API[✓ Allowed APIs]
Blocked[✗ Blocked]
end
Request --> FW
Force --> Decision
Voluntary --> Decision
Decision -->|Allow| API
Decision -->|Deny| Blocked
style Request fill:#404040
style Decision fill:#00d4ff,color:#0a0a0a
style FW fill:#404040
style Blocked fill:#8b2635
style Drop fill:#8b2635
style API fill:#2a7f62
style Force fill:#2a7f62
style Voluntary fill:#4a7c59
Try it out
cargo install httpjail
And, check out the GitHub repository for more details.