Repository navigation
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
230 lines (229 loc) · 15.1 KB
/
Copy pathdocker-compose.yml
File metadata and controls
230 lines (229 loc) · 15.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
services:
claudish-proxy:
build: .
container_name: ${CLAUDISH_CONTAINER_NAME:-claudish-proxy}
restart: unless-stopped
# How long Docker waits after SIGTERM before SIGKILL.
#
# This is the bound on the proxy's graceful shutdown, and the ONLY one: the
# SIGTERM handler in `standalone-proxy.ts` waits for in-flight responses to
# finish without an internal deadline, deliberately — a timer there would be
# a second, invisible answer to a question this field already answers, and
# an operator who needs out now sends the signal twice.
#
# Docker's default is 10s. A response stream lasts E[S] ~ 18s (measured on
# the hub over 45.9h, M/G/inf on 95706 samples), so the default would kill
# the majority of them mid-body and surface client-side as the very error
# the graceful path exists to remove: `Connection lost mid-response`.
# 120s covers the distribution with margin; it is paid only at shutdown, and
# only for as long as streams are actually still running.
stop_grace_period: 120s
ports:
# Host port is per-machine: 3000 is already taken on some sidecar hosts.
# Container side stays 3000 — see `command:` below.
- "${CLAUDISH_HOST_PORT:-3000}:3000"
volumes:
# Mount config from host (contains API keys, routing, profiles). Defaults to
# the hub (po-2023) path so the hub compose keeps working with no .env at all;
# a sidecar sets CLAUDISH_CONFIG_DIR to its own profile, e.g.
# C:\Users\<user>\.claudish
- type: bind
source: ${CLAUDISH_CONFIG_DIR:-C:\Users\jsboi\.claudish}
target: /root/.claudish
read_only: true
# OpenAI Codex OAuth tokens: hub-only bind, deliberately NOT here — a
# file-type bind whose source is absent breaks (or directory-poisons)
# every sidecar compose up. The hub carries it in its local
# docker-compose.override.yml instead (auto-merged, never committed).
# Persistent diagnostic capture: full request/response bodies for offline
# analysis. The proxy writes req-*.json / resp-*.sse here (gated by
# CLAUDISH_CAPTURE_DIR). Compacted nightly by ClaudishCaptureCompaction
# (compress-captures.ps1), then backed up to GDrive. Override
# CLAUDISH_CAPTURE_HOST_DIR on machines without a D: drive.
# Sidecars KEEP capture on: in NOMINAL mode the relay path writes nothing
# (the hub captures centrally), so any loose req-*/resp-* file on a sidecar
# is by construction an AUTONOMOUS-mode outage capture — which is exactly
# what reconcile-outage-captures.ps1 packs and merges back. Setting
# CLAUDISH_CAPTURE_DIR empty disables writing entirely; that is an escape
# hatch for disk-starved hosts, NOT the sidecar default, because it would
# silently destroy the outage-reconciliation trail.
# NB: CLAUDISH_CAPTURE_HOST_DIR (host bind) is deliberately NOT named
# CLAUDISH_CAPTURES_DIR — a one-letter delta from the container-side
# CLAUDISH_CAPTURE_DIR below would be a silent-misconfiguration trap.
- type: bind
source: ${CLAUDISH_CAPTURE_HOST_DIR:-D:\claudish-captures}
target: /captures
environment:
- NODE_ENV=production
- CLAUDISH_PROXY_KEY=${CLAUDISH_PROXY_KEY:-}
# WebSearch interception endpoint. Public URL sits behind IIS Basic Auth
# (2026-08-19): WAN machines embed credentials in the URL userinfo
# (https://user:pass@search.myia.io — parsed by claudish, never logged),
# LAN machines point at the backend directly (no auth). Default to LAN
# direct so a fresh deployment works out of the box — the public URL
# 401s without creds. Never commit credentials here.
# (runbook _intercom/2026-07-18_runbook-searxng-protection-po2023.md §9-§10).
- SEARXNG_URL=${SEARXNG_URL:-http://192.168.0.47:8181}
# Empty disables capture writing (sidecar default; hub leaves it at /captures).
- CLAUDISH_CAPTURE_DIR=${CLAUDISH_CAPTURE_DIR-/captures}
# Sidecar relay (fork extension). All empty → this container is the HUB
# (po-2025 since the 2026-09 migration): always local, unchanged. On a
# sidecar machine, set these in the per-machine .env:
# CLAUDISH_RELAY_UPSTREAM — hub URL (LAN http://192.168.0.50:3000, WAN https://models.myia.io)
# CLAUDISH_RELAY_COMPRESS — 1 on a WAN upstream only
# CLAUDISH_NO_ANTHROPIC — 1 on every machine except ai-01 (leak-policy backstop)
# CLAUDISH_RELAY_HEADER_TIMEOUT_MS — relay header budget (#320); empty = the
# 360s hub-contract default, NEVER 0 (Number("") trap, #310 review)
- CLAUDISH_RELAY_UPSTREAM=${CLAUDISH_RELAY_UPSTREAM:-}
- CLAUDISH_RELAY_COMPRESS=${CLAUDISH_RELAY_COMPRESS:-}
- CLAUDISH_RELAY_HEADER_TIMEOUT_MS=${CLAUDISH_RELAY_HEADER_TIMEOUT_MS:-}
- CLAUDISH_NO_ANTHROPIC=${CLAUDISH_NO_ANTHROPIC:-}
# Canonical fleet id (e.g. myia-po-2024), written into the sidecar .env by
# `install-sidecar.ps1 -Machine`. Attached to THIS process's own liveness
# probe (relay deepProbe) so hub attribution files it as the machine's
# traffic instead of a header-less phantom lane (#345). Empty = probes stay
# header-less — the hub's own case, where the relay branch (and therefore
# the probe) never runs. Listed here because the env file only
# interpolates: a name absent from this list never reaches the container.
- CLAUDISH_MACHINE=${CLAUDISH_MACHINE:-}
# Native credential guard kill switch (#296): only an explicit "0" disarms;
# empty = guard on. Listed here because the env file only interpolates —
# a name absent from this list never reaches the container.
- CLAUDISH_NATIVE_FOREIGN_TOKEN_GUARD=${CLAUDISH_NATIVE_FOREIGN_TOKEN_GUARD:-}
# #218 kill switch (native model pin): same absence class as the guard
# above — it postdated this list, so "=0" in a .env never reached a
# container (#304). Only an explicit "0" disarms; empty = pin active.
- CLAUDISH_NATIVE_MODEL_PIN=${CLAUDISH_NATIVE_MODEL_PIN:-}
# #305 AC-1 instrument: "1" logs the auth header NAMES a native request
# carries (never values). Off when empty. Read per request, but like every
# name here it enters the container only at a recreate.
- CLAUDISH_NATIVE_HEADER_NAMES_LOG=${CLAUDISH_NATIVE_HEADER_NAMES_LOG:-}
# Budget failover (fork extension). All empty → inert: no routing change,
# no notice, zero added bytes. Belongs on the machine that actually CALLS
# the models. That is the hub most of the time, but a sidecar calls them
# too, in AUTONOMOUS mode, which is precisely when the hub cannot help.
# This comment used to end "i.e. the hub, since a NOMINAL sidecar only
# relays". On the ai-01 sidecar these are in fact all empty.
# Measured on ai-01 over 5 days: 124 requests served locally, 70 done,
# 54 lost to one walled lane with no cascade to fall to. Arm these here
# too. Arming alone is not enough: roleFromModelName() matches only
# opus|sonnet|haiku|fable, so a bare provider id (glm-5.2) resolves to null
# and the cascade stays blind. Set CLAUDISH_FAILOVER_ROLE_MODELS, or
# have clients name roles neutrally.
# See CLAUDE.md § "Budget Failover".
- CLAUDISH_FAILOVER_OPUS=${CLAUDISH_FAILOVER_OPUS:-}
- CLAUDISH_FAILOVER_OPUS_LABEL=${CLAUDISH_FAILOVER_OPUS_LABEL:-}
- CLAUDISH_FAILOVER_OPUS_DIRECTION=${CLAUDISH_FAILOVER_OPUS_DIRECTION:-}
- CLAUDISH_FAILOVER_OPUS_NOTE=${CLAUDISH_FAILOVER_OPUS_NOTE:-}
- CLAUDISH_FAILOVER_OPUS_RESET=${CLAUDISH_FAILOVER_OPUS_RESET:-}
- CLAUDISH_FAILOVER_SONNET=${CLAUDISH_FAILOVER_SONNET:-}
- CLAUDISH_FAILOVER_SONNET_LABEL=${CLAUDISH_FAILOVER_SONNET_LABEL:-}
- CLAUDISH_FAILOVER_SONNET_DIRECTION=${CLAUDISH_FAILOVER_SONNET_DIRECTION:-}
- CLAUDISH_FAILOVER_SONNET_NOTE=${CLAUDISH_FAILOVER_SONNET_NOTE:-}
- CLAUDISH_FAILOVER_SONNET_RESET=${CLAUDISH_FAILOVER_SONNET_RESET:-}
- CLAUDISH_FAILOVER_HAIKU=${CLAUDISH_FAILOVER_HAIKU:-}
- CLAUDISH_FAILOVER_HAIKU_LABEL=${CLAUDISH_FAILOVER_HAIKU_LABEL:-}
- CLAUDISH_FAILOVER_HAIKU_DIRECTION=${CLAUDISH_FAILOVER_HAIKU_DIRECTION:-}
- CLAUDISH_FAILOVER_HAIKU_NOTE=${CLAUDISH_FAILOVER_HAIKU_NOTE:-}
- CLAUDISH_FAILOVER_HAIKU_RESET=${CLAUDISH_FAILOVER_HAIKU_RESET:-}
- CLAUDISH_FAILOVER_FABLE=${CLAUDISH_FAILOVER_FABLE:-}
- CLAUDISH_FAILOVER_FABLE_LABEL=${CLAUDISH_FAILOVER_FABLE_LABEL:-}
- CLAUDISH_FAILOVER_FABLE_DIRECTION=${CLAUDISH_FAILOVER_FABLE_DIRECTION:-}
- CLAUDISH_FAILOVER_FABLE_NOTE=${CLAUDISH_FAILOVER_FABLE_NOTE:-}
- CLAUDISH_FAILOVER_FABLE_RESET=${CLAUDISH_FAILOVER_FABLE_RESET:-}
- CLAUDISH_FAILOVER_ROLE_MODELS=${CLAUDISH_FAILOVER_ROLE_MODELS:-}
- CLAUDISH_FAILOVER_ACTIVE=${CLAUDISH_FAILOVER_ACTIVE:-}
- CLAUDISH_FAILOVER_AUTO=${CLAUDISH_FAILOVER_AUTO:-}
# #304 — knobs read by initFailover() that postdated the 23-name list
# above. The env file only interpolates: a value written for a name
# absent from this list never reaches the process, and the proxy runs on
# its defaults whatever the operator wrote. Documented (with values) in
# .env.sidecar.example. compose-env-coverage.test.ts pins code↔compose.
# NB these are STARTUP reads (initFailover), unlike the THINKING knobs
# below — a flip needs a recreate, not just a file edit.
- CLAUDISH_FAILOVER_ARM_AFTER=${CLAUDISH_FAILOVER_ARM_AFTER:-}
- CLAUDISH_FAILOVER_ARM_GRACE_MS=${CLAUDISH_FAILOVER_ARM_GRACE_MS:-}
- CLAUDISH_FAILOVER_ARM_RETRY_AFTER_CEILING_MS=${CLAUDISH_FAILOVER_ARM_RETRY_AFTER_CEILING_MS:-}
- CLAUDISH_FAILOVER_SESSION_DWELL_MS=${CLAUDISH_FAILOVER_SESSION_DWELL_MS:-}
- CLAUDISH_FAILOVER_RECOVERY_GRACE_MS=${CLAUDISH_FAILOVER_RECOVERY_GRACE_MS:-}
# #299-B — one-shot overload walk kill switch. Unlike the knobs above this
# is a read-per-request switch (the fleet flips it mid-flight; no recreate),
# but a container still needs the NAME here to ever see any value at all.
- CLAUDISH_FAILOVER_OVERLOAD_WALK=${CLAUDISH_FAILOVER_OVERLOAD_WALK:-}
# #348 — overload-walk time budget (ms). Default 300000; 0 = off. Same
# read-per-request class as the switch above, same container rule.
- CLAUDISH_FAILOVER_WALK_BUDGET_MS=${CLAUDISH_FAILOVER_WALK_BUDGET_MS:-}
# #317 — per-session output-token ceiling (option B). Both empty → inert
# (no counting, no refusal, zero added bytes). The WARN-only pose ships
# first: set CLAUDISH_SESSION_BUDGET_WARN on the hub, leave CEILING empty
# until the user fixes the values. Read-per-request — but a container
# needs the NAME here to ever see a value (#304).
- CLAUDISH_SESSION_BUDGET_CEILING=${CLAUDISH_SESSION_BUDGET_CEILING:-}
- CLAUDISH_SESSION_BUDGET_WARN=${CLAUDISH_SESSION_BUDGET_WARN:-}
# #431 — step-skip kill switch: "0" restores the pre-#431 behavior (an
# overflowed/busy cascade step surfaces its recovery turn / waits
# unbounded instead of skipping forward). Read-per-request.
- CLAUDISH_FAILOVER_STEP_SKIP=${CLAUDISH_FAILOVER_STEP_SKIP:-}
# #431 — how long a CASCADE STEP attempt waits for an endpoint
# concurrency slot before skipping forward (ms). Default 2000; 0 = off
# (unbounded FIFO). The nominal path never reads it. Read-per-request.
- CLAUDISH_FAILOVER_STEP_BUSY_WAIT_MS=${CLAUDISH_FAILOVER_STEP_BUSY_WAIT_MS:-}
# #310 — runtime knobs the proxy reads that were never in this list
# (same absence class as the #304 failover knobs above: the env file only
# interpolates, so a value set for a name this list does not carry never
# reached a container). NOTE: ${VAR:-} makes each name PRESENT with value
# "" — every read site was verified for "" ≡ unset (review #310): 20 held
# by construction, 2 were fixed in that review (OVERFLOW_REPORT_FLOOR,
# CATALOG_URL), and compose-env-coverage.test.ts now refuses a
# `?? <non-empty>` read on any name this list injects. Explicit exemption
# list there for CLI-only names (CLAUDISH_MODEL, CLAUDISH_HOST,
# CLAUDISH_MCP_TOOLS, the statusline pair, the launcher TTL,
# CLAUDISH_TEST_*).
# Never-hang & recovery bounds. Re-read per request/stream INSIDE the
# process; a NEW value enters a container only at its next recreate.
- CLAUDISH_FIRST_EVENT_TIMEOUT_MS=${CLAUDISH_FIRST_EVENT_TIMEOUT_MS:-}
- CLAUDISH_PREVISIBLE_REFORWARD_MAX=${CLAUDISH_PREVISIBLE_REFORWARD_MAX:-}
- CLAUDISH_CONNECT_RETRY_MAX=${CLAUDISH_CONNECT_RETRY_MAX:-}
- CLAUDISH_CONNECT_RETRY_DELAYS_MS=${CLAUDISH_CONNECT_RETRY_DELAYS_MS:-}
- CLAUDISH_STALL_THRESHOLD_MS=${CLAUDISH_STALL_THRESHOLD_MS:-}
# Auth & native lane: key-rotation overlap + #219 auto-pin.
- CLAUDISH_PROXY_KEY_PREVIOUS=${CLAUDISH_PROXY_KEY_PREVIOUS:-}
- CLAUDISH_NATIVE_PIN_AUTO=${CLAUDISH_NATIVE_PIN_AUTO:-}
- CLAUDISH_NATIVE_PIN_AUTO_INTERVAL_MS=${CLAUDISH_NATIVE_PIN_AUTO_INTERVAL_MS:-}
# Routing, catalog & capability vocabulary (#83 gate, default off).
- CLAUDISH_DEFAULT_PROVIDER=${CLAUDISH_DEFAULT_PROVIDER:-}
- CLAUDISH_CATALOG_URL=${CLAUDISH_CATALOG_URL:-}
- CLAUDISH_CAPABILITY_VOCAB=${CLAUDISH_CAPABILITY_VOCAB:-}
- CLAUDISH_CAPABILITY_QUERY_MAX_ASKS=${CLAUDISH_CAPABILITY_QUERY_MAX_ASKS:-}
# Local lane (lmstudio/ollama reached through the host gateway).
- CLAUDISH_CONTEXT_WINDOW=${CLAUDISH_CONTEXT_WINDOW:-}
- CLAUDISH_LOCAL_QUEUE_ENABLED=${CLAUDISH_LOCAL_QUEUE_ENABLED:-}
- CLAUDISH_LOCAL_MAX_PARALLEL=${CLAUDISH_LOCAL_MAX_PARALLEL:-}
- CLAUDISH_QWEN_NO_THINK=${CLAUDISH_QWEN_NO_THINK:-}
# Observability toggles (0/false/off disables).
- CLAUDISH_TELEMETRY=${CLAUDISH_TELEMETRY:-}
- CLAUDISH_STATS=${CLAUDISH_STATS:-}
- CLAUDISH_OVERFLOW_REPORT_FLOOR=${CLAUDISH_OVERFLOW_REPORT_FLOOR:-}
- CLAUDISH_SWAP_ADVISOR=${CLAUDISH_SWAP_ADVISOR:-}
- CLAUDISH_SWAP_ADVISOR_LOG=${CLAUDISH_SWAP_ADVISOR_LOG:-}
- CLAUDISH_SWAP_ADVISOR_DUMP=${CLAUDISH_SWAP_ADVISOR_DUMP:-}
# Qwen reasons by default and bills on output. disabled (default) |
# passthrough | budget:<n>. Re-read per request: flip without a restart.
- CLAUDISH_QWEN_THINKING=${CLAUDISH_QWEN_THINKING:-}
# GLM thinking is binary on the coding wire. passthrough (default) |
# disabled. Re-read per request: flip without a restart.
- CLAUDISH_GLM_THINKING=${CLAUDISH_GLM_THINKING:-}
# MiniMax honors the client's thinking value exactly (CC sends disabled
# for haiku/subagents). passthrough (default) | disabled | forced[:<n>].
# Re-read per request: flip without a restart.
- CLAUDISH_MINIMAX_THINKING=${CLAUDISH_MINIMAX_THINKING:-}
# DeepSeek reasons by default and PAYG bills on output tokens.
# passthrough (default) | disabled. Re-read per request.
- CLAUDISH_DEEPSEEK_THINKING=${CLAUDISH_DEEPSEEK_THINKING:-}
command: ["--port", "3000", "--host", "0.0.0.0"]
healthcheck:
test: ["CMD", "wget", "-qO-", "http://0.0.0.0:3000/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s