Remove TUI legacy Windows sandbox dependency (#27490)

## Why

This is part of an ongoing attempt to eliminate the TUI's direct
dependency on core features. When we moved the TUI to the app server, we
left a `legacy_core` shim that re-exported some remaining core symbols
for the TUI. The intent was to eventually remove all of these.

In this PR, we remove the symbols related to the Windows sandbox.

The change should be behavior-neutral and low risk because it's just
refactoring and removal of code that is now effectively dead.

When working on this PR, I noticed a big existing problem that affects
mixed-platform remoting. For example, if you run the TUI on a Linux box
and remote into a Windows box, the TUI logic doesn't properly handle
Windows sandbox setup properly. Fixing this is beyond the scope of this
PR, but I've left a TODO comment in place so we don't forget.

## What changed

- Move the remaining TUI-specific sandbox level, setup, telemetry, and
read-root helpers into `codex-tui`, calling `codex-windows-sandbox`
directly.
- Remove the Windows sandbox namespace and read-root grant re-exports
from the client-side `legacy_core` facade.
- Remove the dormant pre-elevation prompt fallback guarded by the
permanently enabled `ELEVATED_SANDBOX_NUX_ENABLED` switch. The reachable
elevated and non-elevated setup flows remain unchanged.
This commit is contained in:
Eric Traut
2026-06-11 09:23:08 -07:00
committed by GitHub
Unverified
parent f42780109c
commit 1e5b87b4d7
13 changed files with 170 additions and 123 deletions
-5
View File
@@ -77,7 +77,6 @@ pub use crate::remote::RemoteAppServerEndpoint;
pub mod legacy_core {
pub use codex_core::check_execpolicy_for_warnings;
pub use codex_core::format_exec_policy_error_with_source;
pub use codex_core::grant_read_root_non_elevated;
pub mod config {
pub use codex_core::config::*;
@@ -86,10 +85,6 @@ pub mod legacy_core {
pub use codex_core::config::edit::*;
}
}
pub mod windows_sandbox {
pub use codex_core::windows_sandbox::*;
}
}
const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5);
+1 -3
View File
@@ -51,8 +51,6 @@ use crate::legacy_core::config::ConfigBuilder;
use crate::legacy_core::config::ConfigOverrides;
use crate::legacy_core::config::PermissionProfileSnapshot;
use crate::legacy_core::config::edit::ConfigEditsBuilder;
#[cfg(target_os = "windows")]
use crate::legacy_core::windows_sandbox::WindowsSandboxLevelExt;
use crate::model_catalog::ModelCatalog;
use crate::model_migration::ModelMigrationOutcome;
use crate::model_migration::migration_copy_for_models;
@@ -1063,7 +1061,7 @@ See the Codex keymap documentation for supported actions and examples."
#[cfg(target_os = "windows")]
{
let startup_permission_profile = app.config.permissions.effective_permission_profile();
let should_check = WindowsSandboxLevel::from_config(&app.config)
let should_check = crate::windows_sandbox::level_from_config(&app.config)
!= WindowsSandboxLevel::Disabled
&& managed_filesystem_sandbox_is_restricted(&startup_permission_profile)
&& !app
+1 -1
View File
@@ -964,7 +964,7 @@ impl App {
fn propagate_windows_sandbox_turn_context(&self) {
#[cfg(target_os = "windows")]
{
let windows_sandbox_level = WindowsSandboxLevel::from_config(&self.config);
let windows_sandbox_level = crate::windows_sandbox::level_from_config(&self.config);
self.app_event_tx
.send(AppEvent::CodexOp(AppCommand::override_turn_context(
/*cwd*/ None,
+9 -13
View File
@@ -949,9 +949,7 @@ impl App {
// If the elevated setup already ran on this machine, don't prompt for
// elevation again - just flip the config to use the elevated path.
if crate::legacy_core::windows_sandbox::sandbox_setup_is_complete(
codex_home.as_path(),
) {
if crate::windows_sandbox::sandbox_setup_is_complete(codex_home.as_path()) {
tx.send(AppEvent::EnableWindowsSandboxForAgentMode {
preset,
mode: WindowsSandboxEnableMode::Elevated,
@@ -964,7 +962,7 @@ impl App {
self.windows_sandbox.setup_started_at = Some(Instant::now());
let session_telemetry = self.session_telemetry.clone();
tokio::task::spawn_blocking(move || {
let result = crate::legacy_core::windows_sandbox::run_elevated_setup(
let result = crate::windows_sandbox::run_elevated_setup(
&permission_profile,
workspace_roots.as_slice(),
command_cwd.as_path(),
@@ -988,9 +986,7 @@ impl App {
let mut code_tag: Option<String> = None;
let mut message_tag: Option<String> = None;
if let Some((code, message)) =
crate::legacy_core::windows_sandbox::elevated_setup_failure_details(
&err,
)
crate::windows_sandbox::elevated_setup_failure_details(&err)
{
code_tag = Some(code);
message_tag = Some(message);
@@ -1003,7 +999,7 @@ impl App {
tags.push(("message", message));
}
session_telemetry.counter(
crate::legacy_core::windows_sandbox::elevated_setup_failure_metric_name(
crate::windows_sandbox::elevated_setup_failure_metric_name(
&err,
),
/*inc*/ 1,
@@ -1074,12 +1070,12 @@ impl App {
self.chat_widget.show_windows_sandbox_setup_status();
tokio::task::spawn_blocking(move || {
if let Err(err) =
crate::legacy_core::windows_sandbox::run_legacy_setup_preflight(
codex_windows_sandbox::run_windows_sandbox_legacy_preflight(
&permission_profile,
workspace_roots.as_slice(),
codex_home.as_path(),
command_cwd.as_path(),
&env_map,
codex_home.as_path(),
)
{
session_telemetry.counter(
@@ -1123,7 +1119,7 @@ impl App {
tokio::task::spawn_blocking(move || {
let requested_path = PathBuf::from(path);
let event = match crate::legacy_core::grant_read_root_non_elevated(
let event = match crate::windows_sandbox::grant_read_root_non_elevated(
&permission_profile,
workspace_roots.as_slice(),
command_cwd.as_path(),
@@ -1219,7 +1215,7 @@ impl App {
self.config.permissions.windows_sandbox_mode,
);
let windows_sandbox_level =
WindowsSandboxLevel::from_config(&self.config);
crate::windows_sandbox::level_from_config(&self.config);
if let Some((sample_paths, extra_count, failed_scan)) =
self.chat_widget.world_writable_warning_details()
{
@@ -1562,7 +1558,7 @@ impl App {
return Ok(AppRunControl::Continue);
}
let should_check = WindowsSandboxLevel::from_config(&self.config)
let should_check = crate::windows_sandbox::level_from_config(&self.config)
!= WindowsSandboxLevel::Disabled
&& permission_profile_is_managed_restricted
&& !self.chat_widget.world_writable_warning_hidden();
+1 -3
View File
@@ -60,8 +60,6 @@ use crate::diff_model::FileChange;
use crate::git_action_directives::parse_assistant_markdown;
use crate::legacy_core::config::Config;
use crate::legacy_core::config::PermissionProfileSnapshot;
#[cfg(any(target_os = "windows", test))]
use crate::legacy_core::windows_sandbox::WindowsSandboxLevelExt;
use crate::mention_codec::LinkedMention;
use crate::mention_codec::encode_history_mentions;
use crate::model_catalog::ModelCatalog;
@@ -265,7 +263,7 @@ use crate::app_event::AppEvent;
use crate::app_event::ExitMode;
use crate::app_event::PermissionProfileSelection;
use crate::app_event::RateLimitRefreshOrigin;
#[cfg(any(target_os = "windows", test))]
#[cfg(target_os = "windows")]
use crate::app_event::WindowsSandboxEnableMode;
use crate::app_event_sender::AppEventSender;
use crate::auto_review_denials;
+6 -7
View File
@@ -251,13 +251,12 @@ impl ChatWidget {
.bottom_pane
.set_queued_message_edit_binding(widget.queued_message_edit_hint_binding);
#[cfg(target_os = "windows")]
widget.bottom_pane.set_windows_degraded_sandbox_active(
crate::legacy_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED
&& matches!(
WindowsSandboxLevel::from_config(&widget.config),
WindowsSandboxLevel::RestrictedToken
),
);
widget
.bottom_pane
.set_windows_degraded_sandbox_active(matches!(
crate::windows_sandbox::level_from_config(&widget.config),
WindowsSandboxLevel::RestrictedToken
));
widget.update_collaboration_mode_indicator();
widget
@@ -29,7 +29,7 @@ impl ChatWidget {
let presets: Vec<ApprovalPreset> = builtin_approval_presets();
#[cfg(target_os = "windows")]
let windows_sandbox_level = WindowsSandboxLevel::from_config(&self.config);
let windows_sandbox_level = crate::windows_sandbox::level_from_config(&self.config);
#[cfg(target_os = "windows")]
let windows_degraded_sandbox_enabled =
matches!(windows_sandbox_level, WindowsSandboxLevel::RestrictedToken);
@@ -37,9 +37,7 @@ impl ChatWidget {
let windows_degraded_sandbox_enabled = false;
let show_elevate_sandbox_hint =
crate::legacy_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED
&& windows_degraded_sandbox_enabled
&& presets.iter().any(|preset| preset.id == "auto");
windows_degraded_sandbox_enabled && presets.iter().any(|preset| preset.id == "auto");
let guardian_disabled_reason = |enabled: bool| {
let mut next_features = self.config.features.get().clone();
@@ -326,13 +324,13 @@ impl ChatWidget {
if approvals_reviewer == ApprovalsReviewer::User && preset.id == "auto" {
#[cfg(target_os = "windows")]
{
if WindowsSandboxLevel::from_config(&self.config) == WindowsSandboxLevel::Disabled {
if crate::windows_sandbox::level_from_config(&self.config)
== WindowsSandboxLevel::Disabled
{
let preset = preset.clone();
if crate::legacy_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED
&& crate::legacy_core::windows_sandbox::sandbox_setup_is_complete(
self.config.codex_home.as_path(),
)
{
if crate::windows_sandbox::sandbox_setup_is_complete(
self.config.codex_home.as_path(),
) {
return vec![Box::new(move |tx| {
tx.send(AppEvent::EnableWindowsSandboxForAgentMode {
preset: preset.clone(),
+10 -14
View File
@@ -58,13 +58,11 @@ impl ChatWidget {
pub(crate) fn set_windows_sandbox_mode(&mut self, mode: Option<WindowsSandboxModeToml>) {
self.config.permissions.windows_sandbox_mode = mode;
#[cfg(target_os = "windows")]
self.bottom_pane.set_windows_degraded_sandbox_active(
crate::legacy_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED
&& matches!(
WindowsSandboxLevel::from_config(&self.config),
WindowsSandboxLevel::RestrictedToken
),
);
self.bottom_pane
.set_windows_degraded_sandbox_active(matches!(
crate::windows_sandbox::level_from_config(&self.config),
WindowsSandboxLevel::RestrictedToken
));
}
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
@@ -121,13 +119,11 @@ impl ChatWidget {
feature,
Feature::WindowsSandbox | Feature::WindowsSandboxElevated
) {
self.bottom_pane.set_windows_degraded_sandbox_active(
crate::legacy_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED
&& matches!(
WindowsSandboxLevel::from_config(&self.config),
WindowsSandboxLevel::RestrictedToken
),
);
self.bottom_pane
.set_windows_degraded_sandbox_active(matches!(
crate::windows_sandbox::level_from_config(&self.config),
WindowsSandboxLevel::RestrictedToken
));
}
enabled
}
@@ -317,12 +317,11 @@ impl ChatWidget {
SlashCommand::ElevateSandbox => {
#[cfg(target_os = "windows")]
{
let windows_sandbox_level = WindowsSandboxLevel::from_config(&self.config);
let windows_sandbox_level =
crate::windows_sandbox::level_from_config(&self.config);
let windows_degraded_sandbox_enabled =
matches!(windows_sandbox_level, WindowsSandboxLevel::RestrictedToken);
if !windows_degraded_sandbox_enabled
|| !crate::legacy_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED
{
if !windows_degraded_sandbox_enabled {
// This command should not be visible/recognized outside degraded mode,
// but guard anyway in case something dispatches it directly.
return;
@@ -968,7 +967,7 @@ impl ChatWidget {
fn builtin_command_flags(&self) -> BuiltinCommandFlags {
#[cfg(target_os = "windows")]
let allow_elevate_sandbox = {
let windows_sandbox_level = WindowsSandboxLevel::from_config(&self.config);
let windows_sandbox_level = crate::windows_sandbox::level_from_config(&self.config);
matches!(windows_sandbox_level, WindowsSandboxLevel::RestrictedToken)
};
#[cfg(not(target_os = "windows"))]
@@ -15,7 +15,7 @@ impl ChatWidget {
#[cfg(any(target_os = "windows", test))]
pub(super) fn elevated_windows_sandbox_setup_required(&self) -> bool {
WindowsSandboxLevel::from_config(&self.config) == WindowsSandboxLevel::Elevated
crate::windows_sandbox::level_from_config(&self.config) == WindowsSandboxLevel::Elevated
&& self
.config
.config_layer_stack
@@ -23,9 +23,7 @@ impl ChatWidget {
.windows_sandbox_mode
.source
.is_some()
&& !crate::legacy_core::windows_sandbox::sandbox_setup_is_complete(
self.config.codex_home.as_path(),
)
&& !crate::windows_sandbox::sandbox_setup_is_complete(self.config.codex_home.as_path())
}
#[cfg(target_os = "windows")]
@@ -226,54 +224,6 @@ impl ChatWidget {
) {
use ratatui_macros::line;
if !crate::legacy_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED {
// Legacy flow (pre-NUX): explain the experimental sandbox and let the user enable it
// directly (no elevation prompts).
let mut header = ColumnRenderable::new();
header.push(*Box::new(
Paragraph::new(vec![
line!["Agent mode on Windows uses an experimental sandbox to limit network and filesystem access.".bold()],
line!["Learn more: https://developers.openai.com/codex/windows"],
])
.wrap(Wrap { trim: false }),
));
let preset_clone = preset;
let items = vec![
SelectionItem {
name: "Enable experimental sandbox".to_string(),
description: None,
actions: vec![Box::new(move |tx| {
tx.send(AppEvent::EnableWindowsSandboxForAgentMode {
preset: preset_clone.clone(),
mode: WindowsSandboxEnableMode::Legacy,
profile_selection: profile_selection.clone(),
});
})],
dismiss_on_select: true,
..Default::default()
},
SelectionItem {
name: "Go back".to_string(),
description: None,
actions: vec![Box::new(|tx| {
tx.send(AppEvent::OpenApprovalsPopup);
})],
dismiss_on_select: true,
..Default::default()
},
];
self.bottom_pane.show_selection_view(SelectionViewParams {
title: None,
footer_hint: Some(standard_popup_hint_line()),
items,
header: Box::new(header),
..Default::default()
});
return;
}
self.session_telemetry.counter(
"codex.windows_sandbox.elevated_prompt_shown",
/*inc*/ 1,
@@ -508,7 +458,7 @@ impl ChatWidget {
#[cfg(target_os = "windows")]
pub(crate) fn maybe_prompt_windows_sandbox_enable(&mut self, show_now: bool) {
let windows_sandbox_level = WindowsSandboxLevel::from_config(&self.config);
let windows_sandbox_level = crate::windows_sandbox::level_from_config(&self.config);
let setup_is_required = windows_sandbox_level == WindowsSandboxLevel::Disabled
|| self.elevated_windows_sandbox_setup_required();
if show_now
+4 -6
View File
@@ -11,8 +11,6 @@ use crate::legacy_core::config::load_config_as_toml_with_cli_and_load_options;
use crate::legacy_core::config::resolve_oss_provider;
use crate::legacy_core::config::resolve_profile_v2_config_path;
use crate::legacy_core::format_exec_policy_error_with_source;
#[cfg(target_os = "windows")]
use crate::legacy_core::windows_sandbox::WindowsSandboxLevelExt;
use crate::session_resume::ResolveCwdOutcome;
use crate::session_resume::resolve_cwd_for_resume_or_fork;
pub use crate::startup_error::LocalStateDbStartupError;
@@ -212,6 +210,8 @@ mod version;
#[cfg(not(target_os = "linux"))]
mod voice;
mod width;
#[cfg(any(target_os = "windows", test))]
mod windows_sandbox;
mod workspace_command;
#[cfg(target_os = "linux")]
#[allow(dead_code)]
@@ -1741,7 +1741,7 @@ async fn run_ratatui_app(
set_default_client_residency_requirement(config.enforce_residency.value());
let should_show_trust_screen = should_show_trust_screen(&config);
#[cfg(target_os = "windows")]
let windows_sandbox_level = WindowsSandboxLevel::from_config(&config);
let windows_sandbox_level = crate::windows_sandbox::level_from_config(&config);
#[cfg(target_os = "windows")]
let required_elevated_sandbox_needs_setup = windows_sandbox_level
== WindowsSandboxLevel::Elevated
@@ -1751,9 +1751,7 @@ async fn run_ratatui_app(
.windows_sandbox_mode
.source
.is_some()
&& !crate::legacy_core::windows_sandbox::sandbox_setup_is_complete(
config.codex_home.as_path(),
);
&& !crate::windows_sandbox::sandbox_setup_is_complete(config.codex_home.as_path());
#[cfg(target_os = "windows")]
let should_prompt_windows_sandbox_nux_at_startup = (trust_decision_was_made
&& windows_sandbox_level == WindowsSandboxLevel::Disabled)
@@ -36,8 +36,6 @@ use crate::config_update::format_config_error;
use crate::config_update::write_trusted_project;
use crate::key_hint::KeyBindingListExt;
use crate::legacy_core::config::Config;
#[cfg(target_os = "windows")]
use crate::legacy_core::windows_sandbox::WindowsSandboxLevelExt;
use crate::onboarding::auth::AuthModeWidget;
use crate::onboarding::auth::SignInOption;
use crate::onboarding::auth::SignInState;
@@ -143,7 +141,7 @@ impl OnboardingScreen {
}
#[cfg(target_os = "windows")]
let show_windows_create_sandbox_hint =
WindowsSandboxLevel::from_config(&config) == WindowsSandboxLevel::Disabled;
crate::windows_sandbox::level_from_config(&config) == WindowsSandboxLevel::Disabled;
#[cfg(not(target_os = "windows"))]
let show_windows_create_sandbox_hint = false;
let highlighted = TrustDirectorySelection::Trust;
+122
View File
@@ -0,0 +1,122 @@
//! TUI-owned Windows sandbox helpers retained while setup still runs in the local client process.
//!
//! TODO: These helpers inspect and modify the TUI host, so they do not support
//! cross-platform remote app servers. Move readiness and setup to the existing
//! `windowsSandbox/*` RPCs while preserving the pending permission profile,
//! use the server platform reported during initialization, and add a remote
//! equivalent for read-root grants.
use crate::legacy_core::config::Config;
use codex_config::types::WindowsSandboxModeToml;
use codex_features::Feature;
use codex_protocol::config_types::WindowsSandboxLevel;
#[cfg(target_os = "windows")]
use codex_protocol::models::PermissionProfile;
#[cfg(target_os = "windows")]
use codex_utils_absolute_path::AbsolutePathBuf;
#[cfg(target_os = "windows")]
use std::collections::HashMap;
use std::path::Path;
#[cfg(target_os = "windows")]
use std::path::PathBuf;
pub(crate) fn level_from_config(config: &Config) -> WindowsSandboxLevel {
match config.permissions.windows_sandbox_mode {
Some(WindowsSandboxModeToml::Elevated) => WindowsSandboxLevel::Elevated,
Some(WindowsSandboxModeToml::Unelevated) => WindowsSandboxLevel::RestrictedToken,
None if config.features.enabled(Feature::WindowsSandboxElevated) => {
WindowsSandboxLevel::Elevated
}
None if config.features.enabled(Feature::WindowsSandbox) => {
WindowsSandboxLevel::RestrictedToken
}
None => WindowsSandboxLevel::Disabled,
}
}
#[cfg(target_os = "windows")]
pub(crate) use codex_windows_sandbox::sandbox_setup_is_complete;
#[cfg(not(target_os = "windows"))]
pub(crate) fn sandbox_setup_is_complete(_codex_home: &Path) -> bool {
false
}
#[cfg(target_os = "windows")]
pub(crate) fn run_elevated_setup(
permission_profile: &PermissionProfile,
workspace_roots: &[AbsolutePathBuf],
command_cwd: &Path,
env_map: &HashMap<String, String>,
codex_home: &Path,
) -> anyhow::Result<()> {
let permissions = codex_windows_sandbox::ResolvedWindowsSandboxPermissions::try_from_permission_profile_for_workspace_roots(
permission_profile,
workspace_roots,
)?;
codex_windows_sandbox::run_elevated_setup(
codex_windows_sandbox::SandboxSetupRequest {
permissions: &permissions,
command_cwd,
env_map,
codex_home,
proxy_enforced: false,
},
codex_windows_sandbox::SetupRootOverrides::default(),
)
}
#[cfg(target_os = "windows")]
pub(crate) fn elevated_setup_failure_details(err: &anyhow::Error) -> Option<(String, String)> {
let failure = codex_windows_sandbox::extract_setup_failure(err)?;
Some((
failure.code.as_str().to_string(),
codex_windows_sandbox::sanitize_setup_metric_tag_value(&failure.message),
))
}
#[cfg(target_os = "windows")]
pub(crate) fn elevated_setup_failure_metric_name(err: &anyhow::Error) -> &'static str {
if codex_windows_sandbox::extract_setup_failure(err).is_some_and(|failure| {
matches!(
failure.code,
codex_windows_sandbox::SetupErrorCode::OrchestratorHelperLaunchCanceled
)
}) {
"codex.windows_sandbox.elevated_setup_canceled"
} else {
"codex.windows_sandbox.elevated_setup_failure"
}
}
#[cfg(target_os = "windows")]
pub(crate) fn grant_read_root_non_elevated(
permission_profile: &PermissionProfile,
workspace_roots: &[AbsolutePathBuf],
command_cwd: &Path,
env_map: &HashMap<String, String>,
codex_home: &Path,
read_root: &Path,
) -> anyhow::Result<PathBuf> {
if !read_root.is_absolute() {
anyhow::bail!("path must be absolute: {}", read_root.display());
}
if !read_root.exists() {
anyhow::bail!("path does not exist: {}", read_root.display());
}
if !read_root.is_dir() {
anyhow::bail!("path must be a directory: {}", read_root.display());
}
let canonical_root = dunce::canonicalize(read_root)?;
codex_windows_sandbox::run_setup_refresh_with_extra_read_roots(
permission_profile,
workspace_roots,
command_cwd,
env_map,
codex_home,
vec![canonical_root.clone()],
/*proxy_enforced*/ false,
)?;
Ok(canonical_root)
}