Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3f99eec
feat(network-proxy): add embedded OTEL policy audit logging
mcgrew-oai Feb 17, 2026
fac7fb0
Merge branch 'main' into mcgrew/new-network-proxy-logs
mcgrew-oai Feb 17, 2026
f3c4a1a
refactor(network-proxy): reduce HTTP audit helper args to satisfy clippy
mcgrew-oai Feb 17, 2026
ee5368f
refactor(network-proxy): simplify audit metadata wiring
viyatb-oai Feb 18, 2026
14af73e
refactor(network-proxy): clean up audit metadata imports
viyatb-oai Feb 18, 2026
ab4e0cd
refactor(network-proxy): minimize codex session wiring diff
viyatb-oai Feb 18, 2026
9330f98
network-proxy: unify OTel policy events under policy_decision
mcgrew-oai Feb 18, 2026
4f7b791
Update http_proxy.rs
mcgrew-oai Feb 18, 2026
f91fca7
Merge branch 'main' into mcgrew/new-network-proxy-logs
mcgrew-oai Feb 20, 2026
130eb3f
Merge branch 'main' into mcgrew/new-network-proxy-logs
mcgrew-oai Feb 20, 2026
6b5255c
remove attempt_id
mcgrew-oai Feb 20, 2026
42e6cd2
Merge branch 'main' into mcgrew/new-network-proxy-logs
mcgrew-oai Feb 24, 2026
08bdce9
Investigate network proxy policy log
mcgrew-oai Feb 24, 2026
0b9ef22
Fix http proxy unix socket test
mcgrew-oai Feb 24, 2026
b5fccc6
Merge branch 'main' into mcgrew/new-network-proxy-logs
mcgrew-oai Feb 24, 2026
1b92ce3
Merge branch 'main' into mcgrew/new-network-proxy-logs
mcgrew-oai Feb 24, 2026
b64fed3
feat(network-proxy): add embedded OTEL policy audit logging
mcgrew-oai Feb 17, 2026
6161c5f
refactor(network-proxy): reduce HTTP audit helper args to satisfy clippy
mcgrew-oai Feb 17, 2026
b63727d
refactor(network-proxy): simplify audit metadata wiring
viyatb-oai Feb 18, 2026
53a7f03
refactor(network-proxy): clean up audit metadata imports
viyatb-oai Feb 18, 2026
0f87510
refactor(network-proxy): minimize codex session wiring diff
viyatb-oai Feb 18, 2026
5240b8a
network-proxy: unify OTel policy events under policy_decision
mcgrew-oai Feb 18, 2026
a46c755
Update http_proxy.rs
mcgrew-oai Feb 18, 2026
19736f5
remove attempt_id
mcgrew-oai Feb 20, 2026
e3b7ed9
Investigate network proxy policy log
mcgrew-oai Feb 24, 2026
dbd50ed
Fix http proxy unix socket test
mcgrew-oai Feb 24, 2026
3652323
Merge branch 'main' into mcgrew/new-network-proxy-logs
mcgrew-oai Feb 25, 2026
c0a98ab
Resolve codex core lock conflicts
mcgrew-oai Feb 25, 2026
24923ff
Merge branch 'mcgrew/new-network-proxy-logs' of https://github.com/op…
mcgrew-oai Feb 25, 2026
d0cef29
Merge branch 'main' into mcgrew/new-network-proxy-logs
mcgrew-oai Feb 25, 2026
26a1953
Fix metrics service name warning
mcgrew-oai Feb 25, 2026
781faa0
Update MODULE.bazel.lock checksum
mcgrew-oai Feb 25, 2026
f6a4b50
Merge branch 'main' into mcgrew/new-network-proxy-logs
mcgrew-oai Feb 25, 2026
60e545f
Update MODULE.bazel.lock checksums
mcgrew-oai Feb 25, 2026
3845ba3
Merge branch 'mcgrew/new-network-proxy-logs' of https://github.com/op…
mcgrew-oai Feb 25, 2026
0a36e24
Update MODULE.bazel.lock
mcgrew-oai Feb 25, 2026
47f709a
Update MODULE.bazel.lock
mcgrew-oai Feb 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
230 changes: 128 additions & 102 deletions MODULE.bazel.lock

Large diffs are not rendered by default.

834 changes: 341 additions & 493 deletions codex-rs/Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions codex-rs/app-server/src/codex_message_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ use codex_core::auth::login_with_chatgpt_auth_tokens;
use codex_core::config::Config;
use codex_core::config::ConfigOverrides;
use codex_core::config::ConfigService;
use codex_core::config::NetworkProxyAuditMetadata;
use codex_core::config::edit::ConfigEdit;
use codex_core::config::edit::ConfigEditsBuilder;
use codex_core::config::types::McpServerTransportConfig;
Expand Down Expand Up @@ -1751,6 +1752,7 @@ impl CodexMessageProcessor {
None,
None,
managed_network_requirements_enabled,
NetworkProxyAuditMetadata::default(),
)
.await
{
Expand Down
2 changes: 2 additions & 0 deletions codex-rs/cli/src/debug_sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::path::PathBuf;

use codex_core::config::Config;
use codex_core::config::ConfigOverrides;
use codex_core::config::NetworkProxyAuditMetadata;
use codex_core::exec_env::create_env;
use codex_core::landlock::spawn_command_under_linux_sandbox;
#[cfg(target_os = "macos")]
Expand Down Expand Up @@ -223,6 +224,7 @@ async fn run_command_under_sandbox(
None,
None,
managed_network_requirements_enabled,
NetworkProxyAuditMetadata::default(),
)
.await
.map_err(|err| anyhow::anyhow!("failed to start managed network proxy: {err}"))?,
Expand Down
32 changes: 26 additions & 6 deletions codex-rs/core/src/codex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ use codex_hooks::HookResult;
use codex_hooks::Hooks;
use codex_hooks::HooksConfig;
use codex_network_proxy::NetworkProxy;
use codex_network_proxy::NetworkProxyAuditMetadata;
use codex_network_proxy::normalize_host;
use codex_protocol::ThreadId;
use codex_protocol::approvals::ExecPolicyAmendment;
Expand Down Expand Up @@ -877,13 +878,15 @@ impl Session {
network_policy_decider: Option<Arc<dyn codex_network_proxy::NetworkPolicyDecider>>,
blocked_request_observer: Option<Arc<dyn codex_network_proxy::BlockedRequestObserver>>,
managed_network_requirements_enabled: bool,
audit_metadata: NetworkProxyAuditMetadata,
) -> anyhow::Result<(StartedNetworkProxy, SessionNetworkProxyRuntime)> {
let network_proxy = spec
.start_proxy(
sandbox_policy,
network_policy_decider,
blocked_request_observer,
managed_network_requirements_enabled,
audit_metadata,
)
.await
.map_err(|err| anyhow::anyhow!("failed to start managed network proxy: {err}"))?;
Expand Down Expand Up @@ -1198,21 +1201,37 @@ impl Session {

let auth = auth.as_ref();
let auth_mode = auth.map(CodexAuth::auth_mode).map(TelemetryAuthMode::from);
let account_id = auth.and_then(CodexAuth::get_account_id);
let account_email = auth.and_then(CodexAuth::get_account_email);
let originator = crate::default_client::originator().value;
let terminal_type = terminal::user_agent();
let session_model = session_configuration.collaboration_mode.model().to_string();
let mut otel_manager = OtelManager::new(
conversation_id,
session_configuration.collaboration_mode.model(),
session_configuration.collaboration_mode.model(),
auth.and_then(CodexAuth::get_account_id),
auth.and_then(CodexAuth::get_account_email),
session_model.as_str(),
session_model.as_str(),
account_id.clone(),
account_email.clone(),
auth_mode,
crate::default_client::originator().value,
originator.clone(),
config.otel.log_user_prompt,
terminal::user_agent(),
terminal_type.clone(),
session_configuration.session_source.clone(),
);
if let Some(service_name) = session_configuration.metrics_service_name.as_deref() {
otel_manager = otel_manager.with_metrics_service_name(service_name);
}
let network_proxy_audit_metadata = NetworkProxyAuditMetadata {
conversation_id: Some(conversation_id.to_string()),
app_version: Some(env!("CARGO_PKG_VERSION").to_string()),
user_account_id: account_id,
auth_mode: auth_mode.map(|mode| mode.to_string()),
originator: Some(originator),
user_email: account_email,
terminal_type: Some(terminal_type),
model: Some(session_model.clone()),
slug: Some(session_model),
};
config.features.emit_metrics(&otel_manager);
otel_manager.counter(
"codex.thread.started",
Expand Down Expand Up @@ -1319,6 +1338,7 @@ impl Session {
network_policy_decider.as_ref().map(Arc::clone),
blocked_request_observer.as_ref().map(Arc::clone),
managed_network_requirements_enabled,
network_proxy_audit_metadata,
)
.await?;
(Some(network_proxy), Some(session_network_proxy))
Expand Down
1 change: 1 addition & 0 deletions codex-rs/core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ pub mod types;
pub use codex_config::Constrained;
pub use codex_config::ConstraintError;
pub use codex_config::ConstraintResult;
pub use codex_network_proxy::NetworkProxyAuditMetadata;

pub use network_proxy_spec::NetworkProxySpec;
pub use network_proxy_spec::StartedNetworkProxy;
Expand Down
50 changes: 44 additions & 6 deletions codex-rs/core/src/config/network_proxy_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use codex_network_proxy::ConfigState;
use codex_network_proxy::NetworkDecision;
use codex_network_proxy::NetworkPolicyDecider;
use codex_network_proxy::NetworkProxy;
use codex_network_proxy::NetworkProxyAuditMetadata;
use codex_network_proxy::NetworkProxyConfig;
use codex_network_proxy::NetworkProxyConstraints;
use codex_network_proxy::NetworkProxyHandle;
Expand Down Expand Up @@ -106,13 +107,9 @@ impl NetworkProxySpec {
policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
blocked_request_observer: Option<Arc<dyn BlockedRequestObserver>>,
enable_network_approval_flow: bool,
audit_metadata: NetworkProxyAuditMetadata,
) -> std::io::Result<StartedNetworkProxy> {
let state =
build_config_state(self.config.clone(), self.constraints.clone()).map_err(|err| {
std::io::Error::other(format!("failed to build network proxy state: {err}"))
})?;
let reloader = Arc::new(StaticNetworkProxyReloader::new(state.clone()));
let state = NetworkProxyState::with_reloader(state, reloader);
let state = self.build_state_with_audit_metadata(audit_metadata)?;
let mut builder = NetworkProxy::builder().state(Arc::new(state));
if enable_network_approval_flow
&& matches!(
Expand Down Expand Up @@ -142,6 +139,22 @@ impl NetworkProxySpec {
Ok(StartedNetworkProxy::new(proxy, handle))
}

fn build_state_with_audit_metadata(
&self,
audit_metadata: NetworkProxyAuditMetadata,
) -> std::io::Result<NetworkProxyState> {
let state =
build_config_state(self.config.clone(), self.constraints.clone()).map_err(|err| {
std::io::Error::other(format!("failed to build network proxy state: {err}"))
})?;
let reloader = Arc::new(StaticNetworkProxyReloader::new(state.clone()));
Ok(NetworkProxyState::with_reloader_and_audit_metadata(
state,
reloader,
audit_metadata,
))
}

fn apply_requirements(
mut config: NetworkProxyConfig,
requirements: &NetworkConstraints,
Expand Down Expand Up @@ -205,3 +218,28 @@ impl NetworkProxySpec {
(config, constraints)
}
}

#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;

#[test]
fn build_state_with_audit_metadata_threads_metadata_to_state() {
let spec = NetworkProxySpec {
config: NetworkProxyConfig::default(),
constraints: NetworkProxyConstraints::default(),
};
let metadata = NetworkProxyAuditMetadata {
conversation_id: Some("conversation-1".to_string()),
app_version: Some("1.2.3".to_string()),
user_account_id: Some("acct-1".to_string()),
..NetworkProxyAuditMetadata::default()
};

let state = spec
.build_state_with_audit_metadata(metadata.clone())
.expect("state should build");
assert_eq!(state.audit_metadata(), &metadata);
}
}
1 change: 1 addition & 0 deletions codex-rs/network-proxy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ workspace = true
anyhow = { workspace = true }
async-trait = { workspace = true }
clap = { workspace = true, features = ["derive"] }
chrono = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-utils-home-dir = { workspace = true }
codex-utils-rustls-provider = { workspace = true }
Expand Down
39 changes: 39 additions & 0 deletions codex-rs/network-proxy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,45 @@ the decider can auto-allow network requests originating from that command.
**Important:** Explicit deny rules still win. The decider only gets a chance to override
`not_allowed` (allowlist misses), not `denied` or `not_allowed_local`.

## OTEL Audit Events (embedded/managed)

When `codex-network-proxy` is embedded in managed Codex runtime, policy decisions emit structured
OTEL-compatible events with `target=codex_otel.network_proxy`.

Event name:

- `codex.network_proxy.policy_decision`
- emitted for each policy decision (`domain` and `non_domain`).
- `network.policy.scope = "domain"` for host-policy evaluations (`evaluate_host_policy`).
- `network.policy.scope = "non_domain"` for mode-guard/proxy-state checks (including unix-socket guard paths and unix-socket allow decisions).

Common fields:

- `event.name`
- `event.timestamp` (RFC3339 UTC, millisecond precision)
- optional metadata:
- `conversation.id`
- `app.version`
- `user.account_id`
- policy/network:
- `network.policy.scope` (`domain` or `non_domain`)
- `network.policy.decision` (`allow`, `deny`, or `ask`)
- `network.policy.source` (`baseline_policy`, `mode_guard`, `proxy_state`, `decider`)
- `network.policy.reason`
- `network.transport.protocol`
- `server.address`
- `server.port`
- `http.request.method` (defaults to `"none"` when absent)
- `client.address` (defaults to `"unknown"` when absent)
- `network.policy.override` (`true` only when decider-allow overrides baseline `not_allowed`)

Unix-socket block-path audits use sentinel endpoint values:

- `server.address = "unix-socket"`
- `server.port = 0`

Audit events intentionally avoid logging full URL/path/query data.

## Admin API

The admin API is a small HTTP server intended for debugging and runtime adjustments.
Expand Down
Loading
Loading