From f2a0e7453eceaafd83a800fb512c08a3398277dc Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Mon, 20 Jul 2026 15:23:52 -0600 Subject: [PATCH] feat: copy host OAuth tokens into sandbox at launch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Projects ~/.cache/coyote/oauth/ from the host into /home/agent/.cache/coyote/oauth/ inside the sandbox so agents can call OAuth-authenticated providers without re-authenticating. Same trust model as the existing config-dir and vault-password copies. One-way copy (not bind-mount) — matches Docker's universal support surface. Refreshed tokens die with the sandbox instance; run coyote --authenticate inside if a fresh token is needed (Device Flow works via the QR code render). --- src/sandbox/mod.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/sandbox/mod.rs b/src/sandbox/mod.rs index 030f091..d687652 100644 --- a/src/sandbox/mod.rs +++ b/src/sandbox/mod.rs @@ -388,6 +388,26 @@ fn copy_host_files(name: &str) -> Result<()> { ); } + let oauth_tokens_dir = paths::oauth_tokens_path(); + if oauth_tokens_dir.exists() { + let sandbox_oauth_dir = "/home/agent/.cache/coyote/oauth"; + ensure_sandbox_dir(name, sandbox_oauth_dir)?; + let dest = format!("{name}:{sandbox_oauth_dir}/"); + for entry in fs::read_dir(&oauth_tokens_dir) + .with_context(|| format!("Failed to read {}", oauth_tokens_dir.display()))? + { + let entry = entry?; + let path = entry.path(); + sbx_cp(&path.display().to_string(), &dest)?; + } + chown_agent_recursive(name, sandbox_oauth_dir)?; + } else { + debug!( + "Skipping OAuth token copy: {} does not exist", + oauth_tokens_dir.display() + ); + } + match resolve_vault_password_file() { Some(password_file) if password_file.exists() => { let dest_path = host_to_sandbox_path(&password_file, &home_dir, cfg!(windows))?;