You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(proxy): serve on a Unix domain socket (--uds)
`headroom proxy --uds PATH` binds an AF_UNIX socket instead of a TCP port.
Request handling is untouched; this is the transport shell only.
The motivation is the client-side gate in #1779. Claude Code v2.1.196+ parses
ANTHROPIC_BASE_URL and compares the host against api.anthropic.com exactly,
disabling Remote Control, the 1M window, and on-demand tool loading whenever a
loopback proxy is configured. The gate is also satisfied by
ANTHROPIC_UNIX_SOCKET, so a socket transport lets a client keep a first-party
base URL while its traffic still reaches Headroom. Headroom cannot fix this
server-side the way --1m restores the context-1m header, because both the
eligibility check and the Remote Control bridge live in the client.
The socket is useful on its own terms too: no port to collide with, nothing
listening on the network, and access governed by filesystem permissions.
Path handling refuses more than it deletes. It clears a stale socket left by a
crashed proxy, but refuses to start when something is still listening (two
proxies would split traffic), when the path exists as a non-socket (far more
likely a typo'd argument over real data than a leftover), or when the path
exceeds the platform's sun_path limit, which otherwise fails inside bind()
with an error that names nothing. The parent directory is created 0700, which
is the whole access-control boundary since a Unix socket carries no
credentials of its own.
POSIX only: Windows exposes no socket.AF_UNIX to Python and asyncio has no
Windows UDS transport, so the CLI rejects --uds up front and points at --port.
Docs cover the flag, the permission model, and the Claude Code wiring, with
the Remote Control route marked experimental and unverified end-to-end.
Refs #1779
Copy file name to clipboardExpand all lines: docs/content/docs/proxy.mdx
+49Lines changed: 49 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -32,6 +32,7 @@ Telemetry is **local-only and off by default**. `HEADROOM_TELEMETRY=on` (or `--t
32
32
|--------|---------|-------------|
33
33
|`--host`|`127.0.0.1`| Host to bind to |
34
34
|`--port`|`8787`| Port to bind to |
35
+
|`--uds`| None | Serve on a Unix domain socket at this path instead of `--host`/`--port`. POSIX only — Windows has no `AF_UNIX` support in Python or asyncio. See [Serving on a Unix socket](#serving-on-a-unix-socket)|
35
36
|`--workers`|`1`| Number of Uvicorn worker processes |
36
37
|`--limit-concurrency`|`1000`| Maximum concurrent connections before Uvicorn returns 503 |
37
38
|`--max-connections`|`500`| Maximum upstream HTTP connections |
For permanent custom profiles, see the profile definitions in `headroom/agent_savings.py`. Each profile is an `AgentSavingsProfile` dataclass with fields for compression mode, target ratio, turn protection, and pipeline toggles.
195
196
197
+
## Serving on a Unix socket
198
+
199
+
`--uds` binds an `AF_UNIX` socket instead of a TCP port. Request handling is
200
+
identical — only the transport changes.
201
+
202
+
```bash
203
+
headroom proxy --uds ~/.headroom/run/proxy.sock
204
+
```
205
+
206
+
Two reasons to prefer it over a loopback port:
207
+
208
+
-**No port to collide with, and no port exposed.** Access is governed by
209
+
filesystem permissions rather than by anything listening on the network.
210
+
-**Clients that gate features on the API host.** Claude Code v2.1.196+ turns off
211
+
Remote Control, the 1M context window, and on-demand tool loading when
212
+
`ANTHROPIC_BASE_URL` names a host other than `api.anthropic.com`. A socket lets
213
+
the base URL stay first-party while the traffic still arrives at Headroom — see
214
+
[Remote Control unavailable through custom ANTHROPIC_BASE_URL](/docs/troubleshooting#remote-control-unavailable-through-custom-anthropic_base_url).
215
+
216
+
Point any Unix-socket-capable client at the path. With curl:
Copy file name to clipboardExpand all lines: docs/content/docs/troubleshooting.mdx
+31-3Lines changed: 31 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -215,11 +215,39 @@ See [issue #746](https://github.com/headroomlabs-ai/headroom/issues/746) for the
215
215
216
216
## Remote Control unavailable through custom ANTHROPIC_BASE_URL
217
217
218
-
**Symptom**: When Claude Code runs with `ANTHROPIC_BASE_URL` set to a custom host (for example, Headroom), the Remote Control menu is absent.
218
+
**Symptom**: When Claude Code runs with `ANTHROPIC_BASE_URL` set to a custom host (for example, Headroom), the Remote Control menu is absent. Invoking `/remote-control` reports `Remote Control is only available when using Claude via api.anthropic.com`.
219
219
220
-
**Cause**: This is a Claude-side gate. Headroom only receives normal API traffic and can still compress it, but Claude evaluates Remote Control availability before proxy traffic reaches the server.
220
+
**Cause**: A client-side gate, evaluated before any traffic reaches Headroom. Claude Code v2.1.196+ parses `ANTHROPIC_BASE_URL` and compares the host against `api.anthropic.com` exactly, so a loopback address fails it. The check is satisfied by any one of:
221
221
222
-
**Fix**: Use Headroom for normal proxied API sessions, and launch Claude directly (without `ANTHROPIC_BASE_URL`) when you need Claude Remote Control.
222
+
-`ANTHROPIC_BASE_URL` unset, or naming `api.anthropic.com` as its host, or
223
+
-`ANTHROPIC_UNIX_SOCKET` pointing at a Unix domain socket.
224
+
225
+
Note that Remote Control does **not** honor `_CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL`, the flag that re-enables the 1M window and on-demand tool loading — it reads the base URL directly. Headroom cannot re-enable Remote Control from the server side the way `--1m` restores the `context-1m` beta header, because the eligibility decision and the bridge connection both happen in the client.
226
+
227
+
**Fix**: Launch Claude directly, without `ANTHROPIC_BASE_URL`, for sessions that need Remote Control, and use Headroom for the rest.
228
+
229
+
**Alternative (experimental)**: serve Headroom on a Unix socket and hand Claude Code the socket instead of a URL. The base URL stays first-party, so the gate passes, while the traffic still reaches Headroom:
The upstream request to allow loopback proxies is tracked at [anthropics/claude-code#76653](https://github.com/anthropics/claude-code/issues/76653).
223
251
224
252
`ENABLE_TOOL_SEARCH` is unaffected and can stay enabled for context-window savings while routing through Headroom.
0 commit comments