Skip to content

Commit d46c860

Browse files
committed
Fix subprocess proxy guidance
1 parent 0955cf7 commit d46c860

10 files changed

Lines changed: 133 additions & 94 deletions

File tree

README.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ again after editing config or moving services. It
6565
validates config, checks referenced secret files without printing
6666
values, flags stale active-agent/model state, detects suspicious
6767
self-routing into the local model gateway, warns if the Calciforge
68-
service itself has ambient proxy env, checks whether subprocess agents
69-
define explicit proxy env, warns about externally managed agent daemons
68+
service itself has ambient proxy env, flags subprocess agents that explicitly
69+
set proxy env, warns about externally managed agent daemons
7070
whose outbound proxy environment cannot be proven,
7171
validates configured scanner policy files and rule syntax, and can probe
7272
configured agent endpoints. Use `--no-network` for a purely local check.
@@ -75,12 +75,18 @@ Channel-based secret input is intentionally being de-emphasized because
7575
chat transports can retain plaintext values. Prefer the local paste UI
7676
or direct `fnox` input for new secrets.
7777

78-
Calciforge-managed subprocess agents should get proxy environment from their
79-
agent config or installer-generated config. Do not put proxy variables on the
80-
Calciforge daemon itself; that can route Calciforge's own provider and
81-
control-plane traffic through its security proxy. For externally managed agent
82-
daemons that Calciforge does not launch, set plain HTTP proxying on the agent
83-
process or its service manager and validate it against `security-proxy` logs:
78+
Do not put proxy variables on the Calciforge daemon itself; that can route
79+
Calciforge's own provider and control-plane traffic through its security proxy.
80+
Do not assume CLI agents can be wrapped by setting `HTTP_PROXY` or
81+
`HTTPS_PROXY`; Codex, Claude, ACPX, npm-backed adapters, and streaming clients
82+
may use CONNECT, WebSockets, or browser-backed auth flows that the current
83+
proxy cannot inspect and may break. Use OpenAI-compatible gateway routes,
84+
explicit fetch/tool integrations, or tested wrappers for traffic that must pass
85+
through `security-proxy`.
86+
87+
For externally managed agent daemons that Calciforge does not launch, proxying
88+
has to be configured on that daemon or its service manager and validated
89+
against `security-proxy` logs:
8490

8591
```bash
8692
export HTTP_PROXY=http://127.0.0.1:8888
@@ -107,7 +113,6 @@ id = "codex"
107113
kind = "codex-cli"
108114
model = "gpt-5.5"
109115
timeout_ms = 600000
110-
env = { HTTP_PROXY = "http://127.0.0.1:8888", NO_PROXY = "localhost,127.0.0.1,::1" }
111116

112117
[[routing]]
113118
identity = "owner"

crates/calciforge/examples/config.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ args = [
8383
"--skip-git-repo-check",
8484
"-"
8585
]
86-
env = { HTTP_PROXY = "http://127.0.0.1:8888", NO_PROXY = "localhost,127.0.0.1,::1" }
8786
timeout_ms = 600000
8887

8988
[agents.registry]
@@ -99,7 +98,6 @@ id = "dirac"
9998
kind = "dirac-cli"
10099
command = "dirac"
101100
args = ["--yolo", "--json"]
102-
env = { HTTP_PROXY = "http://127.0.0.1:8888", NO_PROXY = "localhost,127.0.0.1,::1" }
103101
timeout_ms = 600000
104102

105103
[agents.registry]

crates/calciforge/src/doctor.rs

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -383,23 +383,23 @@ fn check_proxy_environment_in(env: ProxyEnvironment, report: &mut DoctorReport)
383383
(Some(http), Some(https)) => {
384384
if http == https {
385385
report.warn(format!(
386-
"Current calciforge doctor process has ambient HTTP(S)_PROXY configured ({}); if the service runs with the same env, model-provider and control-plane traffic can route through security-proxy. Prefer explicit proxy env on agent subprocesses instead.",
386+
"Current calciforge doctor process has ambient HTTP(S)_PROXY configured ({}); if the service runs with the same env, model-provider, channel, and control-plane traffic can route through security-proxy. Prefer no ambient proxy on the Calciforge service.",
387387
display_proxy_value(http)
388388
));
389389
} else {
390390
report.warn(format!(
391-
"Current calciforge doctor process has ambient HTTP_PROXY and HTTPS_PROXY configured but they differ; HTTP_PROXY={}, HTTPS_PROXY={}. Prefer no ambient proxy on the Calciforge service and explicit proxy env on agent subprocesses.",
391+
"Current calciforge doctor process has ambient HTTP_PROXY and HTTPS_PROXY configured but they differ; HTTP_PROXY={}, HTTPS_PROXY={}. Prefer no ambient proxy on the Calciforge service.",
392392
display_proxy_value(http),
393393
display_proxy_value(https)
394394
));
395395
}
396396
}
397397
(Some(http), None) => report.warn(format!(
398-
"Current calciforge doctor process has ambient HTTP_PROXY set ({}). Prefer no ambient proxy on the Calciforge service and explicit proxy env on agent subprocesses.",
398+
"Current calciforge doctor process has ambient HTTP_PROXY set ({}). Prefer no ambient proxy on the Calciforge service.",
399399
display_proxy_value(http)
400400
)),
401401
(None, Some(https)) => report.warn(format!(
402-
"Current calciforge doctor process has ambient HTTPS_PROXY set ({}) but HTTP_PROXY is not set. Prefer no ambient proxy on the Calciforge service and explicit proxy env on agent subprocesses.",
402+
"Current calciforge doctor process has ambient HTTPS_PROXY set ({}) but HTTP_PROXY is not set. Prefer no ambient proxy on the Calciforge service.",
403403
display_proxy_value(https)
404404
)),
405405
(None, None) => report.ok(
@@ -455,7 +455,7 @@ fn check_agent_proxy_coverage(
455455

456456
if has_http_proxy(env) {
457457
report.warn(
458-
"Current calciforge doctor process has ambient HTTP_PROXY; subprocess inheritance works if the service has the same env, but it also risks proxying Calciforge's own provider/control-plane calls. Move proxy env to agent-level config or wrappers.",
458+
"Current calciforge doctor process has ambient HTTP_PROXY; subprocess inheritance works only if the service has the same env, and it can break CLI agents that use CONNECT, WebSockets, npm, or browser-backed auth. Prefer no ambient proxy and only wrap agents through tested recipes.",
459459
);
460460
}
461461

@@ -467,28 +467,28 @@ fn check_agent_proxy_coverage(
467467

468468
if incomplete_count > 0 {
469469
report.warn(format!(
470-
"{incomplete_count} subprocess agent(s) define incomplete proxy env; set HTTP_PROXY to the intended security-proxy endpoint or clear proxy env keys"
470+
"{incomplete_count} subprocess agent(s) define partial proxy env; either remove proxy env or use a tested wrapper that the agent supports"
471471
));
472472
}
473473

474-
if complete_count == subprocess_count && clearing_count == 0 && incomplete_count == 0 {
474+
if complete_count > 0 {
475+
report.warn(format!(
476+
"{complete_count} subprocess agent(s) define explicit HTTP_PROXY env; verify the specific CLI supports that proxy path. HTTPS_PROXY/CONNECT traffic is not inspected by security-proxy and may break streaming agents."
477+
));
478+
}
479+
480+
let missing_count = subprocess_agents
481+
.iter()
482+
.filter(|agent| {
483+
!has_complete_agent_proxy_env(agent)
484+
&& !has_incomplete_agent_proxy_env(agent)
485+
&& !clears_agent_proxy_env(agent)
486+
})
487+
.count();
488+
if missing_count > 0 {
475489
report.ok(format!(
476-
"{subprocess_count} subprocess agent(s) define explicit HTTP_PROXY env; verify these point at security-proxy and include NO_PROXY for loopback"
490+
"{missing_count} subprocess agent(s) have no explicit proxy env; use explicit tool/fetch integration or a tested wrapper for traffic that must pass through security-proxy"
477491
));
478-
} else {
479-
let missing_count = subprocess_agents
480-
.iter()
481-
.filter(|agent| {
482-
!has_complete_agent_proxy_env(agent)
483-
&& !has_incomplete_agent_proxy_env(agent)
484-
&& !clears_agent_proxy_env(agent)
485-
})
486-
.count();
487-
if missing_count > 0 {
488-
report.warn(format!(
489-
"{missing_count} subprocess agent(s) lack explicit HTTP_PROXY env; Calciforge no longer supplies ambient proxy env"
490-
));
491-
}
492492
}
493493
}
494494

@@ -1428,7 +1428,7 @@ mod tests {
14281428
}
14291429

14301430
#[test]
1431-
fn subprocess_agent_proxy_coverage_warns_without_complete_proxy_env() {
1431+
fn subprocess_agent_proxy_coverage_accepts_missing_proxy_env() {
14321432
let mut config = base_config();
14331433
config.agents = vec![AgentConfig {
14341434
id: "codex".to_string(),
@@ -1448,13 +1448,13 @@ mod tests {
14481448
);
14491449

14501450
assert!(report.findings.iter().any(|finding| {
1451-
finding.severity == Severity::Warn
1452-
&& finding.message.contains("lack explicit HTTP_PROXY env")
1451+
finding.severity == Severity::Ok
1452+
&& finding.message.contains("have no explicit proxy env")
14531453
}));
14541454
}
14551455

14561456
#[test]
1457-
fn subprocess_agent_proxy_coverage_accepts_complete_agent_proxy_env() {
1457+
fn subprocess_agent_proxy_coverage_warns_on_complete_agent_proxy_env() {
14581458
let mut config = base_config();
14591459
config.agents = vec![AgentConfig {
14601460
id: "dirac".to_string(),
@@ -1484,7 +1484,7 @@ mod tests {
14841484
);
14851485

14861486
assert!(report.findings.iter().any(|finding| {
1487-
finding.severity == Severity::Ok
1487+
finding.severity == Severity::Warn
14881488
&& finding.message.contains("define explicit HTTP_PROXY env")
14891489
}));
14901490
}
@@ -1515,7 +1515,7 @@ mod tests {
15151515

15161516
assert!(report.findings.iter().any(|finding| {
15171517
finding.severity == Severity::Warn
1518-
&& finding.message.contains("define incomplete proxy env")
1518+
&& finding.message.contains("define partial proxy env")
15191519
}));
15201520
}
15211521

docs/MANUAL_INSTALL.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,16 @@ systemctl enable adversary-detector security-gateway clashd
104104
systemctl start adversary-detector security-gateway clashd
105105
```
106106

107-
## Step 5: Set up proxy env vars
107+
## Step 5: Configure optional external-agent proxy env
108+
109+
Do not set `HTTP_PROXY` or `HTTPS_PROXY` globally for Calciforge itself. The
110+
Calciforge service should call providers, channels, and control-plane endpoints
111+
directly unless a stronger host/container boundary is configured.
112+
113+
For an external agent daemon that you have tested with `security-proxy`, set
114+
proxy environment in that daemon's service manager instead of in
115+
`/etc/profile.d`. For example:
108116

109-
Create `/etc/profile.d/calciforge-proxy.sh`:
110117
```bash
111118
export HTTP_PROXY=http://127.0.0.1:8080
112119
export NO_PROXY=localhost,127.0.0.1,10.*.*.*,172.16.*.*,192.168.*.*
@@ -115,11 +122,8 @@ export NO_PROXY=localhost,127.0.0.1,10.*.*.*,172.16.*.*,192.168.*.*
115122
`HTTPS_PROXY` is intentionally omitted from the basic setup because standard
116123
HTTPS proxying uses CONNECT tunnels that Calciforge cannot inspect without a
117124
separate MITM design. Use explicit Calciforge fetch/tool integration for HTTPS
118-
content that must be scanned or rewritten.
119-
120-
```bash
121-
chmod +x /etc/profile.d/calciforge-proxy.sh
122-
```
125+
content that must be scanned or rewritten, or run the agent inside a controlled
126+
container/VM profile that forces egress through Calciforge services.
123127

124128
## Step 6: Set API credentials
125129

docs/agent-adapters.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@ parsing or safety behavior.
5252

5353
Use a recipe when an upstream tool is useful but its protocol is still just a
5454
documented command invocation. Recipes can still be security-aware: Calciforge
55-
owns identity checks, routing, proxy environment, per-run artifact directories,
56-
timeouts, output validation, and audit logs.
55+
owns identity checks, routing, per-run artifact directories, timeouts, output
56+
validation, audit logs, and tested network wrappers where the upstream runtime
57+
supports them.
5758

5859
Use a named adapter when Calciforge needs upstream-specific parsing or safety
5960
defaults that cannot be expressed cleanly as a recipe. Dirac is the current
@@ -95,7 +96,6 @@ args = [
9596
"{message}",
9697
]
9798
timeout_ms = 120000
98-
env = { HTTP_PROXY = "http://127.0.0.1:8888", NO_PROXY = "localhost,127.0.0.1,::1" }
9999
```
100100

101101
The command above must read the actual task from stdin. `{message}` is a safe
@@ -113,7 +113,6 @@ args = [
113113
"{artifact_dir}/image.png",
114114
]
115115
timeout_ms = 180000
116-
env = { HTTP_PROXY = "http://127.0.0.1:8888", NO_PROXY = "localhost,127.0.0.1,::1" }
117116
```
118117

119118
Treat this npcsh command as a recipe to verify against the installed npcsh

docs/codex-openclaw-integration.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ id = "codex"
3030
kind = "codex-cli"
3131
model = "gpt-5.5"
3232
timeout_ms = 600000
33-
env = { HTTP_PROXY = "http://127.0.0.1:8888", NO_PROXY = "localhost,127.0.0.1,::1" }
3433
aliases = ["gpt", "openai"]
3534

3635
[[routing]]
@@ -66,13 +65,14 @@ args = [
6665
"--skip-git-repo-check",
6766
"-",
6867
]
69-
env = { HTTP_PROXY = "http://127.0.0.1:8888", NO_PROXY = "localhost,127.0.0.1,::1" }
7068
```
7169

72-
Do not assume ambient `HTTPS_PROXY` gives Calciforge visibility into encrypted
73-
model traffic. HTTPS clients normally use CONNECT tunnels; use explicit
74-
fetch/tool integration when encrypted content needs scanning or secret
75-
substitution.
70+
Do not wrap Codex CLI with generic `HTTP_PROXY`/`HTTPS_PROXY` unless you have
71+
validated that specific route. Codex uses streaming and browser/OAuth-backed
72+
control-plane calls; the current `security-proxy` does not inspect CONNECT
73+
tunnels and can break those flows. Use Calciforge's OpenAI-compatible gateway,
74+
exec models, or explicit fetch/tool integration for traffic that needs
75+
scanning or secret substitution.
7676

7777
Keep chat-facing Codex agents conservative. `read-only` is the safer
7878
default for general messaging channels. Use `workspace-write` only for

docs/index.md

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@ tool permissions can be checked before traffic leaves the machine.
205205
Ambient `HTTPS_PROXY` is deliberately not presented as full protection:
206206
standard HTTPS proxying uses CONNECT tunnels, so encrypted request bodies
207207
cannot be inspected or rewritten without a separate MITM design.
208+
For agents that do not work with cooperative proxy env, Calciforge's
209+
security boundary shifts to model-gateway routing, explicit MCP/fetch tools,
210+
audited recipe wrappers, or future container/VM isolation profiles that deny
211+
egress except through Calciforge services.
208212

209213
The gateway protects in three places:
210214

@@ -507,9 +511,9 @@ correctly, for first-class adapter support. The working vocabulary is:
507511

508512
- **Recipes** — documented, security-aware command configurations for
509513
local tools such as npcsh, opencode profiles, or one-off media agents.
510-
Recipes can still use Calciforge identity checks, per-agent proxy
511-
environment, timeouts, stdin prompt delivery, stderr redaction, audit
512-
logs, and controlled artifact directories.
514+
Recipes can still use Calciforge identity checks, timeouts, stdin prompt
515+
delivery, stderr redaction, audit logs, controlled artifact directories,
516+
and tested proxy wrappers where the upstream runtime supports them.
513517
- **Adapters** — first-class protocol integrations used when Calciforge
514518
must understand upstream-specific behavior, such as event streams,
515519
final-answer parsing, approval pauses, callbacks, or native session
@@ -542,7 +546,6 @@ kind = "artifact-cli"
542546
command = "/usr/local/bin/npcsh-vixynt-stdin"
543547
args = ["{artifact_dir}/image.png"]
544548
timeout_ms = 180000
545-
env = { HTTP_PROXY = "http://127.0.0.1:8888", NO_PROXY = "localhost,127.0.0.1,::1" }
546549
```
547550

548551
The command above is a recipe shape, not a promise that every npcsh
@@ -682,14 +685,16 @@ unverified, validates configured scanner policy files and rule syntax,
682685
and can probe configured endpoints.
683686
Use `calciforge doctor --no-network` when you want a local-only check.
684687

685-
Calciforge-managed subprocess agents should get proxy environment from their
686-
agent config or installer-generated config. Do not put proxy variables in
687-
`~/.zshrc` for the Calciforge daemon itself; that can route Calciforge's own
688-
provider and control-plane traffic through its security proxy.
688+
Do not put proxy variables in `~/.zshrc` for the Calciforge daemon itself;
689+
that can route Calciforge's own provider and control-plane traffic through
690+
its security proxy. Do not assume CLI agents can be protected by generic
691+
`HTTP_PROXY` or `HTTPS_PROXY`; Codex, Claude, ACPX, npm-backed adapters, and
692+
streaming clients may use CONNECT, WebSockets, or browser-backed auth flows
693+
that the current proxy cannot inspect and may break.
689694

690-
For externally managed agent daemons that Calciforge does not launch, set
691-
plain HTTP proxying on the agent process or its service manager and validate
692-
it against `security-proxy` logs:
695+
For externally managed agent daemons that Calciforge does not launch, configure
696+
a tested proxy path on the agent process or its service manager and validate it
697+
against `security-proxy` logs:
693698

694699
```bash
695700
# External agent process environment

docs/roadmap/placeholder-injection-mode.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ through PlaceholderMap before forwarding. Same code path as
7878
|---|---|---|
7979
| Agent never sees real secret |||
8080
| Works without agent's awareness of Calciforge |||
81-
| Kernel-enforced (agent can't bypass) || ❌ (agent can unset HTTPS_PROXY, but then can't call out) |
81+
| Kernel-enforced (agent can't bypass) || ❌ (agent can bypass cooperative proxy env unless paired with host/container egress controls) |
8282
| Linux only | yes | no — cross-platform |
8383
| Requires root | yes (CAP_BPF) | no |
8484
| Requires recent kernel | yes (5.x+) | no |
@@ -94,11 +94,9 @@ through PlaceholderMap before forwarding. Same code path as
9494
- Agent uploads its own env var to an untrusted endpoint → placeholder
9595

9696
**Things only true eBPF catches:**
97-
- Agent intentionally bypasses HTTPS_PROXY env to talk directly →
98-
placeholder is useless (no upstream knows what it means), but the
99-
agent can't make ANY call. So the failure mode is "agent fails
100-
loudly" rather than "agent leaks secret". Acceptable for our threat
101-
model where the agent is mostly-trusted.
97+
- Agent intentionally bypasses cooperative proxy env to talk directly →
98+
placeholder is useless because no upstream knows what it means. Pair this
99+
mode with host/container egress controls when bypass resistance matters.
102100

103101
**Things neither catches:**
104102
- Agent intentionally exfiltrates the placeholder + asks Calciforge

0 commit comments

Comments
 (0)