feat(core): add structured network approval plumbing and policy decision model (#11672)

### Description
#### Summary
Introduces the core plumbing required for structured network approvals

#### What changed
- Added structured network policy decision modeling in core.
- Added approval payload/context types needed for network approval
semantics.
- Wired shell/unified-exec runtime plumbing to consume structured
decisions.
- Updated related core error/event surfaces for structured handling.
- Updated protocol plumbing used by core approval flow.
- Included small CLI debug sandbox compatibility updates needed by this
layer.

#### Why
establishes the minimal backend foundation for network approvals without
yet changing high-level orchestration or TUI behavior.

#### Notes
- Behavior remains constrained by existing requirements/config gating.
- Follow-up PRs in the stack handle orchestration, UX, and app-server
integration.

---------

Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
This commit is contained in:
viyatb-oaiandCodex authored and GitHub committed 2026-02-14 04:18:12 +00:00
1 parent 854e91e422
commit b527ee2890
47 files changed
+1874 -176

No files matched your search

+7
View File
@@ -1950,6 +1950,13 @@ impl Config {
self.permissions.windows_sandbox_mode
};
}
pub fn managed_network_requirements_enabled(&self) -> bool {
self.config_layer_stack
.requirements_toml()
.network
.is_some()
}
}
pub(crate) fn uses_deprecated_instructions_file(config_layer_stack: &ConfigLayerStack) -> bool {
+33 -8
View File
@@ -1,8 +1,11 @@
use crate::config;
use crate::config_loader::NetworkConstraints;
use async_trait::async_trait;
use codex_network_proxy::BlockedRequestObserver;
use codex_network_proxy::ConfigReloader;
use codex_network_proxy::ConfigState;
use codex_network_proxy::NetworkDecision;
use codex_network_proxy::NetworkPolicyDecider;
use codex_network_proxy::NetworkProxy;
use codex_network_proxy::NetworkProxyConfig;
use codex_network_proxy::NetworkProxyConstraints;
@@ -11,6 +14,7 @@ use codex_network_proxy::NetworkProxyState;
use codex_network_proxy::build_config_state;
use codex_network_proxy::host_and_port_from_network_addr;
use codex_network_proxy::validate_policy_against_constraints;
use codex_protocol::protocol::SandboxPolicy;
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -92,20 +96,41 @@ impl NetworkProxySpec {
})
}
pub async fn start_proxy(&self) -> std::io::Result<StartedNetworkProxy> {
pub async fn start_proxy(
&self,
sandbox_policy: &SandboxPolicy,
policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
blocked_request_observer: Option<Arc<dyn BlockedRequestObserver>>,
enable_network_approval_flow: bool,
) -> 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 proxy = NetworkProxy::builder()
.state(Arc::new(state))
.build()
.await
.map_err(|err| {
std::io::Error::other(format!("failed to build network proxy: {err}"))
})?;
let mut builder = NetworkProxy::builder().state(Arc::new(state));
if enable_network_approval_flow
&& matches!(
sandbox_policy,
SandboxPolicy::ReadOnly { .. } | SandboxPolicy::WorkspaceWrite { .. }
)
{
builder = match policy_decider {
Some(policy_decider) => builder.policy_decider_arc(policy_decider),
None => builder.policy_decider(|_request| async {
// In restricted sandbox modes, allowlist misses should ask for
// explicit network approval instead of hard-denying.
NetworkDecision::ask("not_allowed")
}),
};
}
if let Some(blocked_request_observer) = blocked_request_observer {
builder = builder.blocked_request_observer_arc(blocked_request_observer);
}
let proxy = builder.build().await.map_err(|err| {
std::io::Error::other(format!("failed to build network proxy: {err}"))
})?;
let handle = proxy
.run()
.await