How to Manage the Lifecycle of OpenLogi's Three-Process Model (GUI, Agent, and Overlay)

OpenLogi uses a three-process architecture where the GUI spawns the agent on demand, the agent supervises the overlay via the succession crate, and a global quit flag prevents unwanted respawns during shutdown.

OpenLogi's cross-platform input management system splits responsibilities across three long-running binaries that communicate via tarpc IPC sockets. Understanding how to manage the lifecycle of these processes—GUI (openlogi-desktop), Agent (openlogi-agent), and Overlay (openlogi-overlay)—is essential for deployment, debugging, and custom integrations. This guide walks through the spawn-on-unreachable pattern, overlay supervision, and graceful shutdown mechanisms implemented in the source code.

The Three-Process Architecture

Process Binary Primary Responsibility
GUI openlogi-desktop Renders the interface, polls agent state, forwards user actions
Agent openlogi-agent Owns HID++ input hooks, performs device I/O, supervises overlay
Overlay openlogi-overlay GPUI helper drawing the cursor-centered Actions Ring

According to the OpenLogi source code in [AGENTS.md](https://github.com/AprilNEA/OpenLogi/blob/master/AGENTS.md), this separation isolates privileged HID++ operations in the agent while keeping the UI lightweight and responsive.

GUI-to-Agent Spawn Pattern

When the GUI's IPC client cannot connect to an existing agent socket, it triggers automatic agent spawning. In [crates/openlogi-desktop/src/services/ipc/launch.rs](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-desktop/src/services/ipc/launch.rs), the launch::spawn_agent() function implements this reflex:

// launch.rs – simplified spawn flow
if SUITE_QUITTING.load(Ordering::Relaxed) { return; }

#[cfg(target_os = "macos")] {
    // Try launchctl kickstart → fallback to registration → open
}

let Some(path) = agent_binary_path() else { 
    warn!("Agent binary not found"); 
    return; 
};

launch_agent(&path).ok();

Key behaviors of this spawn logic:

  • Quit flag guard: Checks the atomic SUITE_QUITTING flag before any spawn attempt—if the user has initiated shutdown, no respawn occurs
  • macOS launchd integration: Attempts launchctl kickstart for supervised launches, falling back to open for bundled helpers or direct exec for development builds
  • Cross-platform binary discovery: Resolves agent_binary_path() relative to the GUI executable, then spawns via disclaim::Command::new(path).spawn()

Agent-to-Overlay Supervision

The agent guarantees exactly one overlay instance using the succession crate. Upon startup, openlogi_agent::overlay::spawn() in [crates/openlogi-agent/src/overlay.rs](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-agent/src/overlay.rs) creates a dedicated supervisor thread:

// overlay.rs – core supervisor implementation
let mut supervisor = Supervisor::new(Role::new(dir, "overlay"), mine);

std::thread::Builder::new()
    .name("openlogi-overlay-supervisor".into())
    .spawn(move || {
        let mut spawn = || Command::new(&binary)
            .env(RUN_ENV, mine.get().to_string())
            .spawn();
        
        loop {
            supervisor.tick(&mut spawn, |event| {
                report(event, &helper, &mut pressed)
            });
        }
    });

The supervisor implementation provides:

  • Run-token isolation: Each agent generation sets a unique RUN_ENV value—stale overlays from previous runs are automatically evicted
  • Role-based state directory: Uses succession::Supervisor with a dedicated "overlay" role for persistent coordination
  • Continuous health checking: The tick() loop monitors overlay health and respawns as needed

Graceful Shutdown Flow

OpenLogi implements coordinated shutdown through two complementary mechanisms:

Suite Quit Flag

When the user selects "Quit" from the system tray, [launch.rs](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-desktop/src/services/ipc/launch.rs#L21-L26) calls mark_suite_quitting():

// Sets the global atomic flag
SUITE_QUITTING.store(true, Ordering::Relaxed);

This prevents the GUI's reconnection logic from respawning the agent during shutdown.

Overlay Eviction

The agent's evict_on_quit routine in [overlay.rs](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-agent/src/overlay.rs#L78-L90) requests immediate overlay termination with a 750ms deadline:

// overlay.rs – clean shutdown path
pub fn evict_on_quit(&mut self) {
    self.supervisor.evict_with_deadline(Duration::from_millis(750));
}

This ensures the GPUI process exits promptly, preventing stranded processes in Activity Monitor or task managers.

Development and Debugging Workflows

Disable Automatic Agent Spawn

Set OPENLOGI_DEV_AGENT=0 to prevent GUI-side agent launching, then run a mock agent manually:


# Terminal 1: Start mock agent

$ cargo run -p openlogi-agent --bin openlogi-agent-mock

# Terminal 2: Start GUI (connects to existing mock)

$ OPENLOGI_DEV_AGENT=0 cargo run -p openlogi-desktop

The mock agent in [crates/openlogi-agent/src/bin/mock_agent.rs](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-agent/src/bin/mock_agent.rs) serves scripted device inventory without requiring physical hardware.

Clean Development Cycle

The xtask command automatically stops orphaned processes before launching:


# Ensures no stale agent/overlay before starting fresh GUI

$ cargo run -p openlogi-desktop

Command-Line Quit

Send a quit signal via deep-link:

$ openlogi-cli quit  # Resolves to openlogi://quit

This triggers the full shutdown sequence: mark_suite_quitting() → blocked respawns → evict_on_quit().

Summary

  • GUI drives initial launch: Uses spawn_agent() reflex when IPC socket unreachable, guarded by SUITE_QUITTING flag
  • Agent supervises overlay: succession::Supervisor in overlay.rs guarantees single instance with run-token eviction
  • Cross-platform spawn logic: macOS uses launchctl/open; other platforms use disclaim::Command
  • Deterministic shutdown: 750ms eviction deadline prevents overlay lingering; global quit flag stops respawn loops
  • Developer controls: OPENLOGI_DEV_AGENT environment variable and mock agent enable UI testing without hardware

Frequently Asked Questions

How does the GUI know when to spawn the agent?

The GUI attempts to connect via tarpc IPC socket. When connection fails, launch::spawn_agent() triggers automatically—unless SUITE_QUITTING indicates an active shutdown. This "spawn-on-unreachable" pattern ensures the agent is always available without manual intervention.

What prevents multiple overlay processes from running?

The succession crate's Supervisor struct in [overlay.rs](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-agent/src/overlay.rs) manages this. Each agent run generates a unique token passed via RUN_ENV environment variable. The supervisor evicts any overlay with a mismatched token, guaranteeing exactly one current instance while cleaning up stale processes from previous runs.

How do I debug the three-process model without Logitech hardware?

Use the mock agent: cargo run -p openlogi-agent --bin openlogi-agent-mock. Set OPENLOGI_DEV_AGENT=0 when launching the GUI to prevent automatic agent spawning. This configuration lets you iterate on UI changes without physical devices connected, as the mock serves synthetic device data through the same IPC interface.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →