PRD 0029: Codex host credentials through egress #110

Merged
didericis merged 25 commits from codex/prd-codex-host-credentials into main 2026-06-01 23:16:25 -04:00
4 changed files with 20 additions and 24 deletions
Showing only changes of commit c8ab0c67a8 - Show all commits
+7 -5
View File
@@ -8,7 +8,7 @@ command, default image, and prompt/auth behavior.
from __future__ import annotations
import os
from dataclasses import dataclass
from dataclasses import dataclass, field
from pathlib import Path
from typing import Literal
@@ -71,6 +71,7 @@ class AgentProvisionPlan:
image: str
dockerfile: str
guest_env: dict[str, str]
env_vars: dict[str, str] = field(default_factory=dict)
dirs: tuple[AgentProvisionDir, ...] = ()
files: tuple[AgentProvisionFile, ...] = ()
pre_copy: tuple[AgentProvisionCommand, ...] = ()
@@ -124,17 +125,17 @@ def agent_provision_plan(
) -> AgentProvisionPlan:
runtime = runtime_for(template)
resolved_guest_env = dict(guest_env or {})
env_vars: dict[str, str] = {}
dirs: list[AgentProvisionDir] = []
files: list[AgentProvisionFile] = []
pre_copy: list[AgentProvisionCommand] = []
verify: list[AgentProvisionCommand] = []
if template == PROVIDER_CODEX:
resolved_guest_env.setdefault(
"CODEX_CA_CERTIFICATE",
"/etc/ssl/certs/ca-certificates.crt",
)
env_vars["CODEX_CA_CERTIFICATE"] = "/etc/ssl/certs/ca-certificates.crt"
auth_dir = resolved_guest_env.get("CODEX_HOME", f"{guest_home}/.codex")
if forward_host_credentials:
env_vars["CODEX_HOME"] = auth_dir
dirs.append(AgentProvisionDir(auth_dir))
config_path = f"{auth_dir}/config.toml"
config_file = state_dir / "codex-config.toml"
@@ -177,6 +178,7 @@ def agent_provision_plan(
prompt_mode=runtime.prompt_mode,
image=runtime.image,
dockerfile=dockerfile,
env_vars=env_vars,
guest_env=resolved_guest_env,
dirs=tuple(dirs),
files=tuple(files),
+5
View File
@@ -12,6 +12,7 @@ from __future__ import annotations
import os
from datetime import datetime, timezone
from dataclasses import replace
from pathlib import Path
from ...agent_provider import agent_provision_plan, runtime_for
@@ -231,6 +232,10 @@ def resolve_plan(
forward_host_credentials=provider.forward_host_credentials,
host_env=dict(os.environ),
)
guest_env = dict(agent_provision.guest_env)
for key, val in agent_provision.env_vars.items():
guest_env.setdefault(key, val)
agent_provision = replace(agent_provision, guest_env=guest_env)
return DockerBottlePlan(
spec=spec,
+5 -18
View File
@@ -12,6 +12,7 @@ from __future__ import annotations
import os
from datetime import datetime, timezone
from dataclasses import replace
from pathlib import Path
from ...agent_provider import agent_provision_plan, runtime_for
@@ -128,24 +129,6 @@ def resolve_plan(
if provider.template == "claude" and has_provider_auth:
didericis marked this conversation as resolved Outdated
Outdated
Review

this should also be in the agent provisioner now, assuming we can evaluate has_provider_auth at that stage. If not we'll need a more generic hook to call into here/there should not be any logic specific to an specific type of agent in here anymore.

this should also be in the agent provisioner now, assuming we can evaluate `has_provider_auth` at that stage. If not we'll need a more generic hook to call into here/there should not be any logic specific to an specific type of agent in here anymore.
guest_env.setdefault("CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC", "1")
guest_env.setdefault("DISABLE_ERROR_REPORTING", "1")
if provider.template == "codex":
# Codex is a Rust/rustls client: unlike the Node agents it does
# NOT consult the system trust store or honor NODE_EXTRA_CA_CERTS.
# It reads CODEX_CA_CERTIFICATE (falling back to SSL_CERT_FILE)
# for custom roots, across HTTPS *and* the wss responses channel.
# Point it at the bundle update-ca-certificates rebuilt with the
# egress MITM CA so Codex trusts the proxy and egress can inject
# the host bearer — without this, codex bottles need
# pipelock tls_passthrough, which disables auth injection.
guest_env["CODEX_CA_CERTIFICATE"] = (
"/etc/ssl/certs/ca-certificates.crt"
)
if provider.template == "codex" and provider.forward_host_credentials:
# Smolvm exec process trees do not reliably inherit the image
# user's login environment. Pin CODEX_HOME to the same path
# provision_provider_auth writes so Codex never falls back to a
# root or unset home and shows the sign-in picker.
guest_env["CODEX_HOME"] = "/home/node/.codex"
supervise_plan = None
if bottle.supervise:
1
@@ -189,6 +172,10 @@ def resolve_plan(
forward_host_credentials=provider.forward_host_credentials,
host_env=dict(os.environ),
)
merged_guest_env = dict(agent_provision.guest_env)
for key, val in agent_provision.env_vars.items():
merged_guest_env.setdefault(key, val)
agent_provision = replace(agent_provision, guest_env=merged_guest_env)
return SmolmachinesBottlePlan(
spec=spec,
+3 -1
View File
@@ -42,8 +42,9 @@ class TestAgentProviderRuntime(unittest.TestCase):
self.assertEqual("/tmp/Dockerfile.codex", plan.dockerfile)
self.assertEqual(
"/etc/ssl/certs/ca-certificates.crt",
plan.guest_env["CODEX_CA_CERTIFICATE"],
plan.env_vars["CODEX_CA_CERTIFICATE"],
)
self.assertEqual({}, plan.guest_env)
self.assertEqual(("/home/node/.codex",), tuple(d.guest_path for d in plan.dirs))
self.assertEqual(
("/home/node/.codex/config.toml",),
@@ -70,6 +71,7 @@ class TestAgentProviderRuntime(unittest.TestCase):
"/run/codex-home/auth.json",
{f.guest_path for f in plan.files},
)
self.assertEqual("/run/codex-home", plan.env_vars["CODEX_HOME"])
self.assertEqual(1, len(plan.pre_copy))
self.assertEqual(1, len(plan.verify))
self.assertIn("CODEX_HOME=/run/codex-home", plan.verify[0].argv)