From 92920984bd1dba6c9f6f671e4c6721b9ca2b3057 Mon Sep 17 00:00:00 2001 From: Marco Allegretti Date: Wed, 11 Mar 2026 18:09:38 +0100 Subject: [PATCH] feat(appd): auto-launch weft-file-portal per session spawn_file_portal() checks WEFT_FILE_PORTAL_BIN; if set, creates a per-session Unix socket at /weft/portal-.sock and spawns weft-file-portal with --allow args derived from the same host paths that resolve_preopens() produces for the session. WEFT_FILE_PORTAL_SOCKET is added to the runtime environment so weft-runtime (and the WASM app) can locate the socket. The portal process is killed and the socket cleaned up at the end of supervise(), after the runtime exits and the mount is torn down. When WEFT_FILE_PORTAL_BIN is unset the behaviour is unchanged. --- crates/weft-appd/src/runtime.rs | 41 ++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/crates/weft-appd/src/runtime.rs b/crates/weft-appd/src/runtime.rs index 94486e1..79526f1 100644 --- a/crates/weft-appd/src/runtime.rs +++ b/crates/weft-appd/src/runtime.rs @@ -87,6 +87,32 @@ fn resolve_preopens(app_id: &str) -> Vec<(String, String)> { preopens } +fn portal_socket_path(session_id: u64) -> Option { + let runtime_dir = std::env::var("XDG_RUNTIME_DIR").ok()?; + let dir = PathBuf::from(runtime_dir).join("weft"); + std::fs::create_dir_all(&dir).ok()?; + Some(dir.join(format!("portal-{session_id}.sock"))) +} + +fn spawn_file_portal( + session_id: u64, + allowed_paths: &[(String, String)], +) -> Option<(PathBuf, tokio::process::Child)> { + let bin = std::env::var("WEFT_FILE_PORTAL_BIN").ok()?; + let socket = portal_socket_path(session_id)?; + let mut cmd = tokio::process::Command::new(&bin); + cmd.arg(&socket) + .stdin(std::process::Stdio::null()) + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()); + for (host, _) in allowed_paths { + cmd.arg("--allow").arg(host); + } + let child = cmd.spawn().ok()?; + tracing::info!(session_id, socket = %socket.display(), "file portal spawned"); + Some((socket, child)) +} + pub(crate) async fn supervise( session_id: u64, app_id: &str, @@ -107,6 +133,9 @@ pub(crate) async fn supervise( let (mount_orch, store_override) = crate::mount::MountOrchestrator::mount_if_needed(app_id, session_id); + let preopens = resolve_preopens(app_id); + let portal = spawn_file_portal(session_id, &preopens); + let mut cmd = if systemd_cgroup_available() { let mut c = tokio::process::Command::new("systemd-run"); c.args([ @@ -140,7 +169,11 @@ pub(crate) async fn supervise( cmd.env("WEFT_APP_STORE", root); } - for (host, guest) in resolve_preopens(app_id) { + if let Some((ref sock, _)) = portal { + cmd.env("WEFT_FILE_PORTAL_SOCKET", sock); + } + + for (host, guest) in &preopens { cmd.arg("--preopen").arg(format!("{host}::{guest}")); } @@ -237,6 +270,12 @@ pub(crate) async fn supervise( mount_orch.umount(); + if let Some((ref sock, mut portal_child)) = portal { + let _ = portal_child.kill().await; + let _ = portal_child.wait().await; + let _ = std::fs::remove_file(sock); + } + { let mut reg = registry.lock().await; reg.set_state(session_id, AppStateKind::Stopped);