The two consecutive registry lock acquisitions after READY left a window
where session state was Running but the AppReady broadcast had not yet
been sent. A concurrent reader winning the lock in that window would
observe Running without a corresponding event. Combining into one
acquisition closes the window.
- supervise: in the stdout-read-error-before-READY case, immediately
kill the child process, tear down the file portal, mark the session
Stopped, and return; previously the function fell through to child.wait()
leaving the session in Starting state with no guaranteed cleanup path
- supervise: restructure app_shell binding as a match expression so the
compiler can verify the initial None value is not silently discarded
- weft-runtime: replace Mutex::lock().unwrap() with unwrap_or_else in
the weft:app/ipc send, recv, and connect host functions so a poisoned
mutex does not panic inside the Wasmtime host-call context
- blit_software: replace expect() on softbuffer Context and Surface
creation with log-and-skip so a frame failure does not crash the process
- blit_software: replace NonZeroU32::new(1).unwrap() with NonZeroU32::MIN
- resumed: replace create_window().expect() with a match that calls
event_loop.exit() and returns on failure instead of unwinding
- build_rendering_ctx: return Option<RenderingCtx> instead of panicking
when SoftwareRenderingContext creation fails; callers exit cleanly
- resumed (app-shell): exit without emitting READY when no rendering
context is available so weft-appd observes a clean session failure
- weft-servo-shell: bound gesture forwarding to one active thread at a
time using JoinHandle::is_finished(); excess batches are dropped with
a debug log to prevent unbounded thread creation per event loop tick
- shell_client (both shells): replace post-ensure! unwrap() with expect()
that documents the invariant
- winit backend: client insertion and dispatch_clients failures now log
and continue instead of unwinding the event loop
- winit backend: bind/render/submit failures in the redraw handler skip
the frame and log a warning; the compositor stays running
- DRM backend: same client insertion and dispatch_clients treatment
- DRM device_added: use .context()? instead of unwrap() when inserting
the new device entry after the drm field is known to be initialised
- DRM render_output: use guard-return instead of unwrap() to access drm
state that was already verified non-None lines above
- compositor state: duplicate layer surface mapping from a misbehaving
client logs a warning and returns instead of panicking
servo WebIDL Python codegen is incompatible with Nix hermetic sandbox.
Build weft-servo-shell and weft-app-shell without servo-embed feature
so the VM infrastructure compiles cleanly. The full servo-embed build
is performed separately via cargo on a Linux host.
Add oxalica/rust-overlay flake input; override rustPlatform in
weft-packages.nix with cargo/rustc from rust-bin.stable.1.93.0;
apply overlay in both devShell and nixosConfigurations.weft-vm.
Fixes build failure where nixos-25.05 Rust 1.86.0 was below the
rust-version requirement of wasmtime and other dependencies.
- weft-packages.nix: add weft-servo-shell and weft-app-shell with servo-embed
feature; real sha256 hashes for servo (8e7dc40) and stylo (dca3934) git deps
computed via nix-prefetch-git; add clang/cmake/python3 as native build inputs
for servo packages; set LIBCLANG_PATH; disable doCheck for VM builds
- configuration.nix: import virtualisation/qemu-vm.nix for system.build.vm;
replace hardware.opengl with hardware.graphics (NixOS 24.11); add
weft-servo-shell systemd user service; add servo-shell/app-shell/pack to
environment packages
- infra/vm/build.sh: switch to system.build.vm target (qcow2 attribute not
provided by default NixOS modules)
- infra/vm/run.sh: invoke the VM script produced by system.build.vm
- Replace Mesa-libEGL-devel and Mesa-libGLES-devel (do not exist on
openSUSE) with libglvnd-devel (Khronos EGL/GLES dispatch headers)
- Replace libseat-devel (does not exist on openSUSE) with seatd-devel
Package names confirmed via rpmfind.net against Tumbleweed and Leap
repositories.
In environments where XDG_RUNTIME_DIR/systemd/private exists but the
user systemd session is not functional, systemd_cgroup_available()
returns true and the test script is wrapped with systemd-run, which
fails immediately, causing ChildStdout to return EOF before READY.
Set WEFT_DISABLE_CGROUP=1 for the duration of the test and restore
it on exit.
- IoView/WasiView split: implement IoView for State, remove table()
from WasiView impl
- preopened_dir: new signature takes host path and guest path directly
- LinkerInstance::func_wrap returns Result in wasmtime 30; use a
variable for the clipboard LinkerInstance
- Set PR_SET_NO_NEW_PRIVS before applying the seccomp filter
- Unconditional syscall block uses empty Vec<SeccompRule>
- flake.nix, infra/nixos/: NixOS VM with Mesa, virtio-gpu, Wayland,
systemd user services for compositor and session supervisor
- infra/vm/: QEMU build and run scripts
- .github/workflows/ci.yml: add Linux job to type-check weft-servo-shell
and weft-app-shell with --features servo-embed
- docs/architecture.md, docs/security.md, docs/building.md: replace
stale pre-implementation design documents
- README.md: rewrite to reflect current codebase
- crates/weft-servo-shell/SERVO_PIN.md: update implementation status and add
SpiderMonkey process boundary statement
- examples/org.weft.demo.counter: stateless counter Wasm component
using weft:app/ipc for increment/decrement/reset; built for
wasm32-wasip2 with wit-bindgen 0.53; dark-themed HTML UI
- examples/org.weft.demo.notes: persistent notes Wasm component
using weft:app/ipc + WASI preopened /data dir (fs:rw:app-data);
save/load via newline-delimited IPC protocol; HTML textarea UI
- examples/keys/: committed demo Ed25519 keypair; both packages
signed with weft-pack sign
- workspace Cargo.toml: exclude examples from workspace members
(they target wasm32-wasip2, not the host toolchain)
- SERVO_PIN.md: update deps section and document shell-client display sharing resolved in
full description of connect_with_display implementation
- Add servo/winit/softbuffer as optional deps in weft-servo-shell and
weft-app-shell Cargo.toml, gated on servo-embed feature
- Replace ShellClient::connect() and connect_as_app() with
connect_with_display() and connect_as_app_with_display(), using
Backend::from_foreign_display to share the winit wl_display connection
- Move ShellClient construction into resumed() in both embedders after
winit window and wl_surface are available
- Pass actual wl_surface to create_window instead of None
- Fix pre-existing field name bug: wayland-scanner generates _type for
the reserved keyword arg name=type, not r#type
Add weft-app-shell binary: takes <app_id> <session_id> args, connects to
zweft_shell_manager_v1 as an application window, resolves the app UI URL,
and runs a single Servo WebView in an isolated process. Prints READY to
stdout after the window is initialised so weft-appd can track the session
lifecycle.
weft-appd runtime.rs: after weft-runtime emits READY, spawn weft-app-shell
(WEFT_APP_SHELL_BIN env var) alongside it. The app shell is killed when the
session ends via abort or natural runtime exit.
weft-servo-shell: remove in-process app WebView management. The shell now
manages the system UI WebView only; all app rendering happens in dedicated
weft-app-shell processes.
Wire ShellClient into App so its Wayland event queue is dispatched each
frame via about_to_wait. This ensures configure, focus_changed, and
window_closed events from the compositor are processed. window_closed
now triggers a clean Servo shutdown.
The EGL rendering path (WindowRenderingContext + surfman eglSwapBuffers)
produces frames transparently via Mesa DMA-BUF buffer sharing; no
explicit zwp_linux_dmabuf_v1 code is required in the shell.
Remaining: ZweftShellWindowV1 is created with surface=null; sharing the
winit wl_surface with the shell client connection is not currently
feasible without refactoring to a single shared Wayland connection.
backdrop-filter is fully implemented across two commits:
- marcoallegretti/stylo servo-weft f1ba496: enables backdrop-filter parsing
- marcoallegretti/servo servo-weft 8e7dc40: wires stacking context and display list
- CSS Grid is implemented via Taffy; backdrop-filter is the remaining unimplemented property
- Document the two-step implementation: stylo parsing enable + display list wiring
- Add Stylo fork section with patch template and step-by-step instructions
- Expand per-app process isolation section with clearer implementation detail
Fork: https://github.com/marcoallegretti/servo
Branch: servo-weft
Base rev: 04ca254f843ed650d3e5b14e5693ad51a60cc84b (upstream main 2026-03-11)
Update the Cargo snippet and update policy to match the fork-and-PR workflow.
The READY timeout branch killed the process and returned Ok(()) without
calling remove_abort_sender(), leaving a spent entry in abort_senders.
Add the call to keep it consistent with all other exit paths.
Path::starts_with is component-aware but does not resolve .., so
/allowed/../etc/passwd would pass the check. Add normalize_path() that
lexically resolves . and .. components without touching the filesystem
so the check works on non-existent paths too. Add regression test.
std::fs::write fails when the destination parent does not exist.
Add create_dir_all before the write so apps can store files in
nested paths (e.g. config/sub/settings.json) without pre-creating
directories. Add regression test for the nested-path case.
Previously the supervisor returned Ok(()) without updating the session
state, leaving it in Starting indefinitely. Now it sets state to
Stopped, removes the abort sender, and broadcasts AppState:stopped
before returning, consistent with every other early-exit path.
RUNNING_APPS response now produces a SyncSessions command instead of
individual Launch commands. The embedder retains only WebViews whose
session_id is in the active list (removes stale entries from sessions
that stopped during a disconnect), then creates WebViews for any
sessions not yet tracked. This is idempotent thanks to the existing
contains_key guard in create_app_webview.
When a runtime process exits naturally (not via TerminateApp) the
oneshot Sender remained in abort_senders until shutdown_all().
Add remove_abort_sender() and call it at the normal exit path in
supervise() to release the entry immediately.