Skip to content

feature: reuse skill-scoped managed network proxies for skill scripts#14416

Closed
celia-oai wants to merge 2 commits intomainfrom
dev/cc/multi-proxy
Closed

feature: reuse skill-scoped managed network proxies for skill scripts#14416
celia-oai wants to merge 2 commits intomainfrom
dev/cc/multi-proxy

Conversation

@celia-oai
Copy link
Copy Markdown
Collaborator

@celia-oai celia-oai commented Mar 12, 2026

Summary

This follows up the managed-network override work for skills by wiring those overrides into shell execution. Before this change, skill scripts still reused the session-level managed network proxy, so per-skill allow/deny domain overrides were not applied through a dedicated proxy.

  • add NetworkProxySpec helpers to apply managed_network_override settings and derive a normalized cache key so equivalent specs reuse the same proxy
  • introduce a shared SkillNetworkProxyCache in session services and thread it through session and subagent construction
  • update unix escalation to resolve the owning skill for a script, start or reuse the matching shared proxy, and fall back to the session proxy when no skill override applies
  • add unit coverage for partial override replacement and normalized cache-key reuse

Related:

Testing

tested with a skill that executes fetch_example.sh which fetches from www.example.com.

case 1

with the following skill permission:

permissions:
  network:
    enabled: true

result:

"aggregatedOutput": "\nHTTP_STATUS:000\ncurl: (56) CONNECT tunnel failed, response 403\n",

That's because the program uses the allowed_domain of the default network proxy and www.example.com is not in the allowed_domain.

case 2

with the following permission:

permissions:
  network:
    enabled: true
    allowed_domains:
      - "www.example.com"

result:

<       "aggregatedOutput": "pyenv: cannot rehash: /Users/celia/.pyenv/shims isn't writable\n<!doctype html><html lang=\"en\"><head><title>Example Domain</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><style>body{background:#eee;width:60vw;margin:15vh auto;font-family:system-ui,sans-serif}h1{font-size:1.5em}div{opacity:0.8}a:link,a:visited{color:#348}</style></head><body><div><h1>Example Domain</h1><p>This domain is for use in documentation examples without needing permission. Avoid use in operations.</p><p><a href=\"https://iana.org/domains/example\">Learn more</a></p></div></body></html>\n\nHTTP_STATUS:200\n",

fetched the website correctly.

@celia-oai celia-oai force-pushed the dev/cc/multi-proxy branch 7 times, most recently from ea68e43 to 9640836 Compare March 12, 2026 23:27
@celia-oai celia-oai changed the base branch from main to dev/cc/skill-override March 12, 2026 23:27
@celia-oai celia-oai force-pushed the dev/cc/multi-proxy branch from 9640836 to 1d511cf Compare March 12, 2026 23:46
@celia-oai celia-oai force-pushed the dev/cc/skill-override branch from 19236d6 to 2da4e05 Compare March 13, 2026 03:32
Base automatically changed from dev/cc/skill-override to main March 13, 2026 04:45
@celia-oai celia-oai force-pushed the dev/cc/multi-proxy branch 7 times, most recently from 4925e68 to d0d7ad8 Compare March 13, 2026 23:23
@celia-oai celia-oai changed the title draft feature: reuse skill-scoped managed network proxies for skill scripts Mar 13, 2026
@celia-oai celia-oai marked this pull request as ready for review March 14, 2026 20:00
@celia-oai celia-oai force-pushed the dev/cc/multi-proxy branch 4 times, most recently from 345691e to 9d82ebc Compare March 18, 2026 22:10
Comment thread codex-rs/core/src/config/network_proxy_spec.rs Outdated
) -> Self {
let mut spec = self.clone();
if let Some(allowed_domains) = managed_network_override.allowed_domains.as_ref() {
spec.config.network.allowed_domains = allowed_domains.clone();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why we really need this to be a map rather than a list.

use std::sync::Arc;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct SkillNetworkProxyKey(String);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use a regular struct for this and rely on #[derive(Eq, Hash)]? Though you will need a constructor like shared_skill_proxy_key() that ensures the fields are sorted when this is constructed.

Or if you want to distill it down to a single value, why not use sha2::Digest instead of an arbitrarily long String?

}

struct CoreShellCommandExecutor {
session: Option<Arc<crate::codex::Session>>,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is this None?

async fn prepare_escalated_exec_turn_default_preserves_macos_seatbelt_extensions() {
let cwd = AbsolutePathBuf::from_absolute_path(std::env::temp_dir()).unwrap();
let executor = CoreShellCommandExecutor {
session: None,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's only None in tests, then ideally we would use a dummy Session or something so the business logic doesn't have to deal with the None case.

Comment on lines +1076 to +1085
let Some(session) = self.session.as_ref() else {
return Ok(self.network.clone());
};
let Some(skill) =
find_skill_for_program(session.as_ref(), self.sandbox_policy_cwd.as_path(), program)
.await
else {
return Ok(self.network.clone());
};
if let Some(network) = session.get_or_start_skill_network_proxy(&skill).await? {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can collapse these ifs such that Ok(self.network.clone()) only appears once?

Comment thread codex-rs/core/src/codex_tests.rs Outdated

#[derive(Default)]
pub(crate) struct SkillNetworkProxyCache {
#[cfg_attr(not(unix), allow(dead_code))]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why shouldn't this be available on Windows? I thought what we have is cross-platform?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not not available on windows, it's not called because only unix_escalation path hits it

return Ok(Arc::clone(proxy));
}

let proxy = Arc::new(start().await?);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we find a way to represent this so that we do not have to await while holding the mutex for proxies?

@celia-oai celia-oai force-pushed the dev/cc/multi-proxy branch 3 times, most recently from 297dea1 to 7b29237 Compare March 19, 2026 01:14
@celia-oai celia-oai force-pushed the dev/cc/multi-proxy branch 2 times, most recently from 339faab to 0eb91ed Compare March 19, 2026 01:27
@celia-oai celia-oai force-pushed the dev/cc/multi-proxy branch from ac4c459 to 4fbccee Compare March 19, 2026 02:49
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 2, 2026

Closing this pull request because it has had no updates for more than 14 days. If you plan to continue working on it, feel free to reopen or open a new PR.

@github-actions github-actions Bot closed this Apr 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants