From f322025b3de9ab396b3341c6d2e3c7ce7943f8c8 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Tue, 25 Aug 2026 07:21:24 -0400 Subject: [PATCH] refactor(sandbox): home the runtime directory under the system tmp dir Use /tmp/fabro/runtime for both Docker and Daytona instead of provider-specific roots. A writable /tmp inside the sandbox is already a dependency (commit-message files, exec stop-files), it needs no root-level mkdir for non-root container users, and it makes the two providers uniform. The trailing runtime path component stays load-bearing: materialized blobs at runtime/blobs/{hash}.json are recognized as managed blob references and normalized back to blob:// in durable context. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_012Kmn5jyrdpyCdvcfvmEDvA --- lib/components/fabro-sandbox/src/daytona/mod.rs | 6 +++++- lib/components/fabro-sandbox/src/docker.rs | 6 +++++- lib/components/fabro-workflow/src/artifact.rs | 10 ++++++---- .../fabro-workflow/src/handler/llm/preamble.rs | 12 ++++++------ .../fabro-workflow/tests/it/integration.rs | 4 ++-- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/components/fabro-sandbox/src/daytona/mod.rs b/lib/components/fabro-sandbox/src/daytona/mod.rs index 8e2eaf7af..8eda8b64f 100644 --- a/lib/components/fabro-sandbox/src/daytona/mod.rs +++ b/lib/components/fabro-sandbox/src/daytona/mod.rs @@ -61,7 +61,11 @@ const DAYTONA_BASH_SESSION_REMEDIATION: &str = "Daytona ran the direct command t pub(crate) const WORKING_DIRECTORY: &str = "/home/daytona/workspace"; pub(crate) const REPOS_ROOT: &str = "/home/daytona/repos"; -pub(crate) const RUNTIME_DIRECTORY: &str = "/home/daytona/fabro/runtime"; +// Beneath the system tmp dir so any sandbox user can create it; the +// trailing `runtime` component is load-bearing — materialized blobs at +// `runtime/blobs/{hash}.json` are recognized as managed blob references and +// normalized back to `blob://` in durable context. +pub(crate) const RUNTIME_DIRECTORY: &str = "/tmp/fabro/runtime"; const DEFAULT_SNAPSHOT: &str = "daytona-medium"; pub const DEFAULT_DAYTONA_API_URL: &str = "https://app.daytona.io/api"; pub(crate) const DAYTONA_DASHBOARD_SANDBOXES_URL: &str = diff --git a/lib/components/fabro-sandbox/src/docker.rs b/lib/components/fabro-sandbox/src/docker.rs index 65f36a086..af69d31e0 100644 --- a/lib/components/fabro-sandbox/src/docker.rs +++ b/lib/components/fabro-sandbox/src/docker.rs @@ -49,7 +49,11 @@ const DOCKER_BASH_REQUIREMENT: &str = "Docker sandboxes require /bin/bash for ev pub(crate) const WORKING_DIRECTORY: &str = "/workspace"; pub(crate) const REPOS_ROOT: &str = "/repos"; -pub(crate) const RUNTIME_DIRECTORY: &str = "/fabro/runtime"; +// Beneath the system tmp dir so any container user can create it; the +// trailing `runtime` component is load-bearing — materialized blobs at +// `runtime/blobs/{hash}.json` are recognized as managed blob references and +// normalized back to `blob://` in durable context. +pub(crate) const RUNTIME_DIRECTORY: &str = "/tmp/fabro/runtime"; const DEFAULT_GIT_CLONE_DEPTH: usize = RunCloneSettings::DEFAULT_DEPTH.unsigned_abs() as usize; const GIT_CLONE_TIMEOUT: Duration = Duration::from_mins(5); #[cfg(test)] diff --git a/lib/components/fabro-workflow/src/artifact.rs b/lib/components/fabro-workflow/src/artifact.rs index 42e167930..6d2eb1e79 100644 --- a/lib/components/fabro-workflow/src/artifact.rs +++ b/lib/components/fabro-workflow/src/artifact.rs @@ -1543,12 +1543,13 @@ mod tests { async fn demote_materializes_remote_values_under_sandbox_runtime_directory() { let run_store: RunStoreHandle = make_run_store("prompt-demote-remote").await.into(); let run_dir = tempfile::tempdir().unwrap(); - let env = TestSyncEnv::new(false, "/workspace").with_runtime_directory("/fabro/runtime"); + let env = + TestSyncEnv::new(false, "/workspace").with_runtime_directory("/tmp/fabro/runtime"); let oversized = serde_json::json!("x".repeat(PROMPT_INLINE_VALUE_MAX + 1)); let expected_bytes = serde_json::to_vec(&oversized).unwrap(); let expected_path = format!( - "/fabro/runtime/blobs/{}.json", + "/tmp/fabro/runtime/blobs/{}.json", BlobHash::new(&expected_bytes) ); let mut values = HashMap::from([("dataset".to_string(), oversized)]); @@ -1605,7 +1606,8 @@ mod tests { let blob_hash = run_store.write_blob(&report_bytes).await.unwrap(); let context = Context::new(); context.set("report", fabro_types::format_blob_ref(&blob_hash).into()); - let env = TestSyncEnv::new(false, "/workspace").with_runtime_directory("/fabro/runtime"); + let env = + TestSyncEnv::new(false, "/workspace").with_runtime_directory("/tmp/fabro/runtime"); let run_dir = tempfile::tempdir().unwrap(); let resolved = @@ -1613,7 +1615,7 @@ mod tests { .await .unwrap(); - let expected_path = format!("/fabro/runtime/blobs/{blob_hash}.json"); + let expected_path = format!("/tmp/fabro/runtime/blobs/{blob_hash}.json"); assert_eq!( resolved["report"], serde_json::json!(format!("file://{expected_path}")) diff --git a/lib/components/fabro-workflow/src/handler/llm/preamble.rs b/lib/components/fabro-workflow/src/handler/llm/preamble.rs index d4e2aa6f4..dc01e65ab 100644 --- a/lib/components/fabro-workflow/src/handler/llm/preamble.rs +++ b/lib/components/fabro-workflow/src/handler/llm/preamble.rs @@ -788,7 +788,7 @@ mod tests { "security_findings", large_prompt_value( 1_843_279, - "/fabro/runtime/blobs/findings.json", + "/tmp/fabro/runtime/blobs/findings.json", "{\"findings\":[\n{\"severity\":\"high\"}", ), ); @@ -798,7 +798,7 @@ mod tests { keys::COMMAND_OUTPUT.to_string(), large_prompt_value( 12 * 1024, - "/fabro/runtime/blobs/output.json", + "/tmp/fabro/runtime/blobs/output.json", "first result\nsecond result", ), ); @@ -819,12 +819,12 @@ mod tests { "\n## Completed stages\n", "- **scan**: succeeded\n", " - Script: `scan --json`\n", - " - Output (12.0 KB; full value: `/fabro/runtime/blobs/output.json`)\n", + " - Output (12.0 KB; full value: `/tmp/fabro/runtime/blobs/output.json`)\n", " Preview: first result\n", " second result…\n", "\n## Context\n", "- security_findings (1.8 MB; full value: ", - "`/fabro/runtime/blobs/findings.json`)\n", + "`/tmp/fabro/runtime/blobs/findings.json`)\n", " Preview: {\"findings\":[\n", " {\"severity\":\"high\"}…\n", ) @@ -1703,7 +1703,7 @@ mod tests { "security_findings", large_prompt_value( 1_843_279, - "/fabro/runtime/blobs/findings.json", + "/tmp/fabro/runtime/blobs/findings.json", "{\"findings\": [\n{\"message\": \"a | b\"}]}", ), ); @@ -1718,7 +1718,7 @@ mod tests { assert!(preamble.contains(concat!( "| security_findings | 1.8 MB; full value: ", - "`/fabro/runtime/blobs/findings.json`; Preview: ", + "`/tmp/fabro/runtime/blobs/findings.json`; Preview: ", "{\"findings\": [ {\"message\": \"a \\| b\"}]}… |", ))); assert!(!preamble.contains("fabroLargeValue")); diff --git a/lib/components/fabro-workflow/tests/it/integration.rs b/lib/components/fabro-workflow/tests/it/integration.rs index 6c176f79d..2aee85909 100644 --- a/lib/components/fabro-workflow/tests/it/integration.rs +++ b/lib/components/fabro-workflow/tests/it/integration.rs @@ -10137,7 +10137,7 @@ impl fabro_agent::Sandbox for RemoteMockEnv { } fn runtime_directory(&self) -> Option<&str> { - Some("/fabro/runtime") + Some("/tmp/fabro/runtime") } async fn list_directory( @@ -10445,7 +10445,7 @@ async fn downstream_remote_execution_resolves_response_blob_refs_as_text() { assert!( written .iter() - .all(|(path, _)| path.starts_with("/fabro/runtime/blobs/")), + .all(|(path, _)| path.starts_with("/tmp/fabro/runtime/blobs/")), "nothing is written outside the sandbox runtime blob directory" ); assert!(