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

> Master OpenLogi's three-process model. Learn how the GUI, Agent, and Overlay interact and how to manage their lifecycle effectively for seamless operation.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: internals
- Published: 2026-09-06

---

**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/main/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/main/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:

```rust
// 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/main/crates/openlogi-agent/src/overlay.rs)](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-agent/src/overlay.rs) creates a dedicated supervisor thread:

```rust
// 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/main/launch.rs)](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-desktop/src/services/ipc/launch.rs#L21-L26) calls `mark_suite_quitting()`:

```rust
// 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/main/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**:

```rust
// 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:

```bash

# 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/main/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:

```bash

# Ensures no stale agent/overlay before starting fresh GUI

$ cargo run -p openlogi-desktop

```

### Command-Line Quit

Send a quit signal via deep-link:

```bash
$ 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`](https://github.com/AprilNEA/OpenLogi/blob/main/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()`](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-desktop/src/services/ipc/launch.rs) 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/main/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.