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-<id>.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.
This commit is contained in:
Marco Allegretti 2026-03-11 18:09:38 +01:00
parent c15fe600fc
commit 92920984bd

View file

@ -87,6 +87,32 @@ fn resolve_preopens(app_id: &str) -> Vec<(String, String)> {
preopens
}
fn portal_socket_path(session_id: u64) -> Option<PathBuf> {
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);