Releases: dahai80/fusion-executor
Release list
v0.2.11 — GUI display_info + move_path GuiAction (Issues #43 #44)
Patch release v0.2.11
Fixes the last 2 open issues (#43, #44). Merged via PR #45 (squash).
#43 — Expose scale_factor / display capability query in GuiResult
- New
GuiAction::DisplayInfo{}enumerates active displays viaCGDisplay::active_displays()→ JSON[{id, scale, bounds:{x,y,w,h}, primary}]exposed as additiveGuiResult.displays: Option<String>. - Trusted-independent (no TCC required).
- Shared
compute_scale_factor()now fillsscale_factorininspect_tree+screenshot(was screenshot-only) — removes hardcoded 2.0 reliance.
#44 — Batch mouse-move (waypoint path) GuiAction
- New
GuiAction::MovePath{points, duration_ms}— N-waypoint path in single atomic UDS round-trip (avoids N hover round-trips). Linear interp, 8 frames/segment,<2points reject.
Safety
- 0 new unsafe blocks (core-graphics 0.24 safe wrappers within fe-gui crate-level scope). GuiAction 18→20 variants.
Verification
- 528 Rust + 231 Python (1 skip TCC) tests green.
- clippy
--all-targets -D warningsclean (only upstream block v0.1.6). fmt/ruff clean. maturin builds.
Version
3-point SSOT bump 0.2.10 → 0.2.11.
v0.2.10 — GUI coordinate mapping + batch actions + screenshot masking
What's new
Three GUI features for fusion-osagent (Issues #38, #39, #40):
#38 — Coordinate-mapping (scale_factor)
GuiResult.scale_factor: f32 bridges logical points (GuiAction x/y + AXPosition) ↔ physical pixels (screenshot_width/height). Retina=2.0, non-Retina=1.0. Caller converts pixel = point * scale_factor.
#39 — Batch actions (gui_action_batch)
Sequential multi-action pipeline collecting per-step GuiResult. Non-transactional (single-step failure does not abort later steps).
results = executor.gui_action_batch([
{"kind": "focus_app", "bundle_id": "com.apple.Terminal"},
{"kind": "screenshot"},
])#40 — Screenshot sensitive-region masking (mask_sensitive)
GuiAction::Screenshot gains mask_sensitive: bool (default false). When true, AX-secure text fields (kAXSecureTextFieldSubrole) are pixel-redacted to black in the PNG (Y-flipped, scale_factor-adjusted). Gated on Accessibility trust.
Safety
0 new unsafe blocks. +7 Rust tests, +9 Python tests.
Verification
526 Rust + 222 Python (7 skip TCC) green; clippy --all-targets -D warnings clean; fmt/ruff clean; maturin builds.
v0.2.9
Patch Release v0.2.9
Ships post-v0.2.8 work already merged to main but unreleased.
PR #36 — dedicated IPC_RT perf fix (#5 soak deadlock)
Decouples IPC server fan-out from the shared BLOCKING_RT (8-worker clamp). Root cause of soak_stress.py deadlock: 64 connections × ~2 long tasks = 128+ tasks starving 8 shared workers → dispatch never scheduled → response frames never written → client 30s timeout.
Fix: dedicated IPC_RT (multi-thread, clamp(4,16) workers) for serve_blocking + probe_runtime. BLOCKING_RT stays for in-process run() path. 0 new deps, 0 new unsafe.
PR #37 — proptest + long_stability harness (enterprise caveats #4/#6)
- #4 (no fuzz tooling): cargo-fuzz needs nightly (host stable-only) → proptest on stable — 4 property tests × 256 cases over
SecurityGuard::validate()(panic-safety, dangerous-prefix block, ReDoS <1s, compound whitelist invariant). - #6 (no long stability test):
scripts/long_stability.py— N-min sustained mixed exec+stream traffic vs realserve()subprocess, 8-worker concurrency, RSS sampling (no-leak <200MB, no-hang 60s budget, error-rate <1%).
RSS watchdog test fix
rss_watchdog_kills_memory_bomb / rss_watchdog_streaming_path failing: macOS memory compression compresses the 1GB virtual bomb to ~200MB physical RSS, below the 256MB threshold → watchdog correctly did NOT fire. Watchdog logic correct (physical RSS = true OOM metric). Fix: threshold 256MB → 64MB (3× margin, preserves OOM semantics).
Verify
cargo fmt --all -- --check✓cargo clippy --workspace --all-targets -- -D warnings✓ (only upstreamblock v0.1.6future-incompat)cargo test --workspace— 519 passedmaturin develop --release—fusion_executor-0.2.9-cp314wheelruff check . && ruff format --check .✓pytest python/tests— 220 passed, 1 skipped (TCC)
Full Changelog: v0.2.8...v0.2.9
v0.2.8 — Per-command Sandbox Profile (Issue #34)
Per-command Seatbelt/Sandbox Profile (Issue #34)
Add a per-command configurable SandboxProfile to ExecutionRequest — enables seatbelt/sandbox for the detached executor subprocess (fusion-code G2 sandbox-independence). Default-off / opt-in: None profile preserves the existing fixed profile byte-for-byte.
SandboxProfile fields
network(allow/deny, defaultdeny) —allowomits network denyfilesystem(allow/deny_write/deny, defaultdeny_write) — FS write deny controlexcluded_commands([]) — injected as seatbeltdeny process-exec, sanitizedfail_if_unavailable(false) — fail-closed whensandbox-execabsent
Python usage
from fusion_executor import FusionSandboxExecutor, SandboxProfile
result = FusionSandboxExecutor().run(
"echo hi",
sandbox=SandboxProfile(network="allow", excluded_commands=["rm", "curl"]),
)Properties
- 4-layer additive wiring (fe-sandbox → fe-core → fe-ipc → fe-pyo3/Python)
- 0 new unsafe (reuses fe-sandbox safe wrappers +
whichPATH probe) - Baseline preserved:
sandbox=Nonebyte-identical to v0.2.7 - 510 Rust + 214 Python (6 skip TCC) green, clippy/fmt/ruff clean
Closes #34.
v0.2.7 — Server-Side Deterministic Cancel (Issue #32)
v0.2.7 — Server-Side Deterministic Cancel (Issue #32)
Patch release. Adds server-side deterministic cancel of in-flight execute_stream over UDS, plus a version bump.
Added
- Server-side cancel (
executor.cancel) — cancel an in-flight streaming execution with a deterministic process-tree kill, not a cooperative stop request. Resolves the stream by its JSON-RPC request id, fires aoneshotchannel thatfe-sandbox::run_streaming's biasedtokio::select!is waiting on, then kills the whole process group:killpg(-pgid, SIGINT)→ 500ms grace →killpg(-pgid, SIGKILL)→ ppid-tree descendant walk (RUN-9) forsetsidorphans. The terminalDoneframe returnsexit_code: -1+cancelled: true.fe-sandboxrun_streaming(cfg, cancel_rx)— biased select over exit / cancel / channel-closed.fe-ipcStreamRegistry = Mutex<HashMap<String, oneshot::Sender<()>>>(IPC layer, keepsExecutorstateless per M-ARCH-1). Cancel works cross-connection.ExecutionResult.cancelled: bool(#[serde(default, skip_serializing_if="is_false")]), 4-layer wired.- Python
FusionSandboxExecutor.cancel_stream(stream_id, *, sock_path=None) -> bool.
Changed
- Version bump 0.2.6 → 0.2.7 (3-point SSOT:
Cargo.toml+pyproject.toml+python/fusion_executor/__init__.pyfallback).
Tests
500 Rust + 210 Python (1 skip TCC) green. clippy --all-targets -D warnings clean (only upstream block v0.1.6 future-incompat). fmt / ruff clean. maturin builds.
Notes
v0.2.6
Patch Release v0.2.6
Patch over v0.2.5. Ships fusion-guard Phase 3 executor consumer (Issue #23, PR #30):
- New crate
fe-guard— sync UDS client + local wire-type mirror + rules cache (unsafe_code="deny", 0 new deps). fe-security— guard orchestration + verdict mapping (Block|L4→block, L3→block, Redact→block, Preview→allow, Allow+L1/L2→allow) + degraded fail-closed on daemon down (never fail-open).fe-core—ExecutionResult.guard_action_id+ seatbelt gating E7 + TOCTOU recheck H4 (symlink_metadata, safe Rust).fe-pyo3+ Python —guard_sock/guard_tenantplumbing, guard OFF by default (backward-compatible).
R4 resolution: existing static blocklist + whitelist fence = compile-time fail-closed fallback; DANGEROUS_BINS const not reintroduced.
Verification
- 496 Rust tests + 206 Python tests green (1 TCC skip).
- clippy/fmt/ruff clean (only upstream
block v0.1.6baseline warning). maturin develop --releasebuilds;__version__ == "0.2.6".
Full Changelog: v0.2.5...v0.2.6
v0.3.0-rc0 — Release Candidate (next minor track)
v0.3.0-rc0 — Release Candidate (next minor track)
Pre-release / release candidate. This is NOT the latest stable release. The current stable release remains v0.2.4 (Enterprise-Publishable Upgrade).
This release candidate marks the HEAD of the main branch immediately after the v0.2.4 enterprise hardening shipped. It opens the v0.3.0 stabilization track for the next minor release.
Contents
v0.3.0-rc0 points at the same commit as the shipped stable v0.2.4 (a7a01c7). It captures the enterprise-publishable baseline as the starting point of the v0.3.0 line:
- 11 Rust crates (fe-core / fe-security / fe-sandbox / fe-rollback / fe-diagnostics / fe-gui / fe-telemetry / fe-ipc / fe-tools / fe-shell / fe-pyo3)
- Python 3.11+ bindings via PyO3 0.29 + maturin
- 476 Rust tests (22 suites) + 200 Python tests (1 TCC skip) green
- clippy
--all-targets -D warningsclean (only upstreamblock v0.1.6future-incompat baseline) - fmt / ruff clean;
maturin develop --releasebuilds - CI green on main (both Python + Rust jobs)
What landed in the v0.2.4 baseline (carried into this RC)
Security hardening
- D3-1 inline-interpreter gateway (block
python -c/node -e/ruby -e/perl -eby default; opt-inwith_allow_inline_interpreter) - ARCH-2 resolved-path whitelist + D3-6 fail-closed (
/tmp/python3poisoning rejected; trusted_bin_dirs =/usr/bin//usr/local/bin//opt/homebrew/bin//bin//sbin+ auto VIRTUAL_ENV/bin + current_exe parent +$HOME/.cargo/bin) - D3-2/D3-3 seatbelt default-on 4-layer + filesystem subpath match
- D3-4 per-task RSS watchdog (sysinfo polls child-tree RSS, kill -124,
oom_killed=true; Darwin RLIMIT_AS no-op) - IMPL-7 prompt-injection sanitize chokepoint (file_path / raw_trace / error_type caps 1KB/4KB/256B)
- RUN-12 default bundle allowlist (Terminal/TextEdit/Finder) + IMPL-9 screenshot Screen-Recording TCC split from Accessibility
Runtime hardening
- RUN-4/RUN-10 rlimit NOFILE(1024) / NPROC(512)
- RUN-9 ppid-tree kill (killpg + sysinfo ppid BFS fallback vs setsid orphans)
- RUN-6 git tokio timeout 30s + RUN-7 worktree gitdir (
rev-parse --git-common-dir) - RUN-3 stream_sem=64 separate from exec_sem=16
- RUN-1
py.detachacross remaining block_on sites
Operations
- D6-01 launchd plist + D6-02 latency p50/p95/p99 + D6-03 snapshot inventory
- M-OPS structured logging (JSON fmt + daily rolling
fe.logtee stderr + reload EnvFilter) + Prometheus metrics (UDS text, no HTTP port) + trace_id cross-layer + SIGHUP hot-reload
Architecture
- ARCH-1 seatbelt default True 4-layer + ARCH-4 in-process metrics + ARCH-6 feature flags (gui/telemetry/diagnostics optional)
- IMPL-1 default socket
~/.fusion-executor/fe.sock4-layer + IMPL-2deny_unknown_fieldsRust serde + IMPL-3 Subscription crash marker (ConnectionError vs silent StopIteration)
Integration
examples/08_integrate_fusion_code.pyintegration skeleton (one-way, no fusion-code import) + ARCH-7 caller-circuit contract doc
Deferred
- Issue #23 (fusion-guard Phase 3 cross-project integration) — intentionally out of scope; tracking comment posted. Executor security baseline hardened and ready to receive guard SSOT rules when Phase 3 is taken up.
Status of this RC
This is an rc0 cut from the same commit as the shipped stable v0.2.4. There is no new code beyond v0.2.4 yet — it establishes the v0.3.0 stabilization track. Subsequent rc1/rc2 cuts will incorporate new work on this line ahead of the final v0.3.0 stable release.
Stable release: https://github.com/dahai80/fusion-executor/releases/tag/v0.2.4
v0.2.5
v0.2.5 (patch)
Patch release over v0.2.4.
Changes
- fe-security test fix (PR #28):
allows_pythonandallows_env_prefixnow usepython3instead of barepython. The D3-6 fail-closed PATH guard (ARCH-2 resolved-path whitelist hardening) rejects binaries that cannot be PATH-resolved; barepythonis absent on some dev machines (onlypython3resolves), so those tests failed locally while CI stayed green (CI venv shipspython). No production logic changed — fail-closed behavior is correct by design; the test now matches the establishedallows_python_c_inlineconvention.
Version sources synced (3-point SSOT)
Cargo.tomlworkspace.package.version→0.2.5(11 crates inherit viaversion.workspace = true)pyproject.tomlproject.version→0.2.5(maturin wheel version)python/fusion_executor/__init__.pyfallback__version__→0.2.5Cargo.lockregenerated
Verification
- 476 Rust + 200 Python tests green (1 TCC skip — GUI needs Accessibility/Screen-Recording permission, CI skips)
cargo clippy --workspace --all-targets -- -D warningsclean (only upstreamblock v0.1.6future-incompat baseline warning, accepted)cargo fmt --all -- --checkcleanruff check .+ruff format --check .cleanmaturin develop --releasebuilds wheelfusion_executor-0.2.5-cp314-cp314-macosx_11_0_arm64fusion_executor.__version__ == "0.2.5"
Full Changelog: v0.2.4...v0.2.5
v0.2.4 — Enterprise-Publishable Upgrade
v0.2.4 — Enterprise-Publishable Upgrade
Product-readiness audit (audit/fusion-executor-audit-result-product-0827.md, baseline v0.2.3) judged fusion-executor ❌ NOT enterprise-publishable (6 real CRITICAL + 33 MAJOR/MINOR). This release closes all of them, upgrading to enterprise-publishable. All work is on main; 476 Rust + 200 Python (1 TCC skip) tests green, clippy/fmt/ruff clean, maturin develop builds.
Scope
Per user directive: fix all 6 real CRITICAL + 33 MAJOR/MINOR + D3-1 inline-interpreter hardening (opt-in gateway). D3-4 heap limit had no pure-code fix → per-task RSS watchdog (sysinfo, existing dep) kills on memory overage as mitigation. Only excluded cross-project Issue #23 (fusion-guard Phase 3).
Highlights
Security hardening
- D3-1 inline-interpreter gateway —
fe-security::validate_argvblockspython -c/node -e/ruby -e/perl -eby default (binary+flag level, payload-agnostic — prevents agent-driven model one-liner bypass);with_allow_inline_interpreter(true)opt-in for trusted callers. - ARCH-2 resolved-path whitelist —
resolve_binary_pathdouble-check: basename in whitelist AND absolute pathstarts_with(trusted_bin_dirs)(/usr/bin,/usr/local/bin,/opt/homebrew/bin,/bin,/sbin+ auto-registeredVIRTUAL_ENV/bin,current_exeparent,$HOME/.cargo/bin)./tmp/python3poisoning rejected. - D3-6 fail-closed — PATH-resolve failure (None) rejects instead of allowing.
- D3-2 seatbelt default on —
shell_startdefaults seatbelt=true across 4 layers. - D3-3 seatbelt FS subpath match — file-write deny scoped correctly.
- D3-8 socket permission hardening — fail-loud on permission errors.
- D3-5/D3-10 secret redaction in command logs.
- IMPL-7 prompt-injection sanitize — single chokepoint in
slice():sanitize_file_path/sanitize_raw_trace/sanitize_error_typewith size caps (1KB/4KB/256B).
Runtime hardening
- D3-4 per-task RSS watchdog —
fe-sandboxsysinfo polls child-process-tree RSS; over-threshold kill (exit -124,oom_killed=true). Darwin RLIMIT_AS is a no-op, so watchdog replaces rlimit. - RUN-4/RUN-10 rlimit — seatbelt profile + non-seatbelt
setrlimitNOFILE(1024)/NPROC(512). - RUN-9 ppid-tree kill —
kill_treekillpg + sysinfo ppid-tree traversal fallback (defends against setsid-orphaned grandchildren). - RUN-6 git timeout + worktree gitdir —
git()wrapped intokio::time::timeout(30s);resolve_git_dirviarev-parse --git-common-dir(worktree.gitis a file, ENOTDIR fix). - RUN-3 stream/exec sem split —
exec_sem=16(non-stream execute) +stream_sem=64(streaming); long streams no longer starve short commands. - RUN-1 py.detach — all
block_onsites release GIL. - RUN-12 default bundle allowlist —
GuiConfig::default()non-empty safe set (Terminal/TextEdit/Finder);disable_bundle_allowlistopt-in for unrestricted. - IMPL-9 screenshot TCC split — Screen Recording TCC probed separately from Accessibility; early-return before AX gate.
Operations
- D6-01 launchd plist + pidfile template.
- D6-02 latency histograms p50/p95/p99.
- D6-03 snapshot inventory
list_snapshots+ retention. - M-OPS structured logging — JSON
fmtlayer + daily-rollingfe.logtee stderr, runtime-reloadableEnvFilter. - M-OPS Prometheus metrics —
executor.metrics_prometheusUDS text export (no HTTP port, M-SEC-01 UDS-only). - trace_id cross-layer —
ExecutionRequest/Resultcarry trace_id (auto-gen uuid v4) +tracing::span!context. - SIGHUP hot-reload — log level + whitelist extras reload without restart.
Architecture
- ARCH-1 seatbelt default True 4-layer (Python run/run_streaming/shell_start + Rust serde).
- ARCH-4 in-process metrics —
execute_synccounts into the global recorder. - ARCH-6 feature flags —
fe-core[features] default=["gui","telemetry","diagnostics"]; headless--no-default-features. - IMPL-1 default socket
~/.fusion-executor/fe.sock4-layer (M-SEC-01 private 0o700 dir). - IMPL-2
deny_unknown_fieldsRust serde aligned with Pythonextra=forbid. - IMPL-3 Subscription crash marker — unexpected disconnect →
ConnectionErrornot silentStopIteration.
Docs / integration
examples/08_integrate_fusion_code.py— one-way integration skeleton (consumes executor API, does not import fusion-code) +max_consecutive_failurescircuit-breaker demo.- ARCH-7 caller-circuit contract documented (reserved field; caller owns consecutive-failure count).
Stats
- 476 Rust tests green (22 suites)
- 200 Python tests green (1 TCC skip — manual GUI permission path)
- clippy
--all-targets -D warningsclean (only upstreamblock v0.1.6future-incompat) - fmt / ruff clean
maturin develop --releasebuildsfusion_executor._native
Audit remediation
This release supersedes the prior v0.2.3 enterprise-hardening release by resolving the product-readiness audit's full CRITICAL + MAJOR/MINOR set. Audit reports (read-only reference):
audit/fusion-executor-audit-result-product-0827.md— product-readiness (this release's target)
Deferred: cross-project Issue #23 (fusion-guard Phase 3) — out of scope for this repo.
v0.2.3 — Enterprise Hardening
v0.2.3 — Enterprise Hardening Release
This release lands the enterprise audit (audit/fusion-executor-audit-result-0826.md) follow-up: 10 CRITICAL + 15 MAJOR + 12 MINOR fixes, plus ops/observability hardening and glob spec alignment. All 12 GitHub issues closed.
Highlights
v0.2.1 — M-ARCH-1 (architecture)
ShellRegistrymoved out ofExecutorto the IPC/PyO3 layer (Arc<ShellRegistry>).Executorregains strict per-task statelessness — expressed in the type system (shell_starttakes®istry, 3 fns became associated).- Fixes P-PYO3-01: a serve-path
Executorrebuild no longer drops backgroundsh-Nhandles; in-process + serve paths share one registry.
v0.2.2 — Observability & operations
- M-OPS-01 structured logging: JSON
fmtlayer teeing a daily-rollingfe.logfile with stderr; runtime-reloadableEnvFilter(basis for SIGHUP). Log dir resolvesFE_LOG_DIR→~/.fusion-executor/logs/. - M-OPS-02 Prometheus metrics:
executor.metrics_prometheusUDS arm returns text format. No HTTP port opened (M-SEC-01). Counters/gauges:fe_exec_total/fe_exec_success/fe_exec_blocked/fe_exec_timeout/fe_exec_failed/fe_rollback_total/fe_rollback_failed/fe_shell_active/fe_connections. - M-OPS-06 + m-OPS-03 trace_id:
ExecutionRequest/ExecutionResultgaintrace_id(auto-gen uuid v4); carried intracing::span!log context across layers. - m-OPS-02 SIGHUP hot-reload:
kill -HUP <pid>reloads log level (RUST_LOG) + whitelist extras (FUSION_EXECUTOR_EXTRA_WHITELIST) without restart.SecurityGuard.whitelist→ArcSwap<HashSet>(interior mutability, Executor stays stateless); extras rebuild from baseline, never accumulate; dangerous interpreters rejected.
v0.2.3 — #20 glob E1 spec alignment
fe-toolsglob():globset::Glob::new→GlobBuilder::new(pattern).literal_separator(true).build().*/?no longer cross/(matches fusion-event E1 ecosystem glob spec);**still crosses directories.- Bug found + fixed by 3 acceptance tests written before the fix (Rule 9):
src/*.swiftno longer wrongly matchessrc/sub/a.swift.
Verification
cargo fmt --all -- --check # clean
cargo clippy --workspace --all-targets -- -D warnings # 0 errors
cargo test --workspace # 359 passed, 0 failed
pytest python/tests # 184 passed, 1 skipped (TCC)
ruff check . && ruff format --check . # clean
maturin develop --release # fusion_executor-0.2.3
Upgrade notes
- Socket default changed to
~/.fusion-executor/fe.sock(private 0o700 dir, M-SEC-01) — setFUSION_EXECUTOR_SOCKto override. - SIGHUP is reload-only (never shuts down); SIGINT/SIGTERM still stop the server.
- No breaking API changes —
trace_id, metrics, logging are all additive.
Full changelog: see CLAUDE.md version blocks (v0.2.1 / v0.2.2 / v0.2.3).