feat(appd): add weft-appd skeleton crate and service unit

New crate implementing the application daemon entry point:
- crates/weft-appd/Cargo.toml: tokio (current-thread runtime), anyhow,
  sd-notify, tracing dependencies
- crates/weft-appd/src/main.rs: async run() resolves IPC socket path
  from WEFT_APPD_SOCKET or XDG_RUNTIME_DIR/weft/appd.sock; stubs for
  AppRegistry, IpcServer, CompositorClient, RuntimeSupervisor,
  CapabilityBroker, ResourceController per WEFT-OS-APPD-DESIGN.md;
  sd_notify(READY=1) to be sent after IpcServer bind + CompositorClient
  connect
- infra/systemd/weft-appd.service: Type=notify, Requires+After
  weft-compositor.service, After servo-shell.service

Also fix two winit backend issues that were present in the working tree:
- remove spurious mut on display binding (never mutated after init)
- wrap std::env::set_var in unsafe block (required since Rust 1.80)
This commit is contained in:
Marco Allegretti 2026-03-11 01:13:18 +01:00
parent 43269c9be1
commit c7ad2116a0
6 changed files with 162 additions and 3 deletions

72
Cargo.lock generated
View file

@ -881,6 +881,17 @@ dependencies = [
"autocfg",
]
[[package]]
name = "mio"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
dependencies = [
"libc",
"wasi",
"windows-sys 0.61.2",
]
[[package]]
name = "ndk"
version = "0.9.0"
@ -1523,6 +1534,16 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
dependencies = [
"errno",
"libc",
]
[[package]]
name = "slab"
version = "0.4.12"
@ -1720,6 +1741,31 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "tokio"
version = "1.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
dependencies = [
"libc",
"mio",
"pin-project-lite",
"signal-hook-registry",
"tokio-macros",
"windows-sys 0.61.2",
]
[[package]]
name = "tokio-macros"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.9.12+spec-1.1.0"
@ -1906,6 +1952,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.2+wasi-0.2.9"
@ -2186,6 +2238,17 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "weft-appd"
version = "0.1.0"
dependencies = [
"anyhow",
"sd-notify",
"tokio",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "weft-build-meta"
version = "0.1.0"
@ -2202,6 +2265,15 @@ dependencies = [
"tracing-subscriber",
]
[[package]]
name = "weft-servo-shell"
version = "0.1.0"
dependencies = [
"anyhow",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "winapi-util"
version = "0.1.11"

View file

@ -1,5 +1,10 @@
[workspace]
members = ["crates/weft-build-meta", "crates/weft-compositor", "crates/weft-servo-shell"]
members = [
"crates/weft-appd",
"crates/weft-build-meta",
"crates/weft-compositor",
"crates/weft-servo-shell",
]
resolver = "2"
[workspace.package]

View file

@ -0,0 +1,16 @@
[package]
name = "weft-appd"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
[[bin]]
name = "weft-appd"
path = "src/main.rs"
[dependencies]
anyhow = "1.0"
sd-notify = "0.4"
tokio = { version = "1", features = ["rt", "macros", "signal"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

View file

@ -0,0 +1,50 @@
use std::path::PathBuf;
use anyhow::Context;
#[tokio::main(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.init();
run().await
}
async fn run() -> anyhow::Result<()> {
let socket_path = appd_socket_path()?;
tracing::info!(path = %socket_path.display(), "weft-appd IPC socket");
// Wave 5 skeleton entry point.
//
// Components to implement (see docu_dev/WEFT-OS-APPD-DESIGN.md):
// AppRegistry — in-memory map of running app sessions and state
// IpcServer — Unix socket at socket_path, serves servo-shell requests
// CompositorClient — Unix socket client to weft-compositor IPC server
// RuntimeSupervisor — spawns and monitors Wasmtime child processes
// CapabilityBroker — resolves manifest permissions to runtime handles
// ResourceController — configures cgroups via systemd transient units
//
// IPC transport: 4-byte LE length-prefixed MessagePack frames.
// Socket path: /run/user/<uid>/weft/appd.sock (overridable via WEFT_APPD_SOCKET).
//
// sd_notify(READY=1) is sent after IpcServer is bound and CompositorClient
// has established its connection, satisfying Type=notify in weft-appd.service.
anyhow::bail!(
"weft-appd event loop not yet implemented; \
see docu_dev/WEFT-OS-APPD-DESIGN.md"
)
}
fn appd_socket_path() -> anyhow::Result<PathBuf> {
if let Ok(p) = std::env::var("WEFT_APPD_SOCKET") {
return Ok(PathBuf::from(p));
}
let runtime_dir = std::env::var("XDG_RUNTIME_DIR").context("XDG_RUNTIME_DIR not set")?;
Ok(PathBuf::from(runtime_dir).join("weft/appd.sock"))
}

View file

@ -20,7 +20,7 @@ use crate::{
};
pub fn run() -> anyhow::Result<()> {
let mut display = smithay::reexports::wayland_server::Display::<WeftCompositorState>::new()?;
let display = smithay::reexports::wayland_server::Display::<WeftCompositorState>::new()?;
let display_handle = display.handle();
let mut event_loop: EventLoop<'static, WeftCompositorState> = EventLoop::try_new()?;
@ -57,7 +57,7 @@ pub fn run() -> anyhow::Result<()> {
let listening_socket = ListeningSocketSource::new_auto()
.map_err(|e| anyhow::anyhow!("Wayland socket creation failed: {e}"))?;
let socket_name = listening_socket.socket_name().to_os_string();
std::env::set_var("WAYLAND_DISPLAY", &socket_name);
unsafe { std::env::set_var("WAYLAND_DISPLAY", &socket_name) };
tracing::info!(?socket_name, "Wayland compositor socket open");
loop_handle

View file

@ -0,0 +1,16 @@
[Unit]
Description=WEFT Application Daemon
Documentation=https://github.com/weft-os/weft
Requires=weft-compositor.service
After=weft-compositor.service servo-shell.service
[Service]
Type=notify
ExecStart=/packages/system/weft-appd/active/bin/weft-appd
Restart=on-failure
RestartSec=1s
# sd_notify(READY=1) is sent after IpcServer is bound and CompositorClient
# has connected, so downstream services can depend on the IPC socket being ready.
[Install]
WantedBy=graphical.target