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_QUITTINGflag before any spawn attempt—if the user has initiated shutdown, no respawn occurs - macOS launchd integration: Attempts
launchctl kickstartfor supervised launches, falling back toopenfor bundled helpers or direct exec for development builds - Cross-platform binary discovery: Resolves
agent_binary_path()relative to the GUI executable, then spawns viadisclaim::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_ENVvalue—stale overlays from previous runs are automatically evicted - Role-based state directory: Uses
succession::Supervisorwith 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 bySUITE_QUITTINGflag - Agent supervises overlay:
succession::Supervisorinoverlay.rsguarantees single instance with run-token eviction - Cross-platform spawn logic: macOS uses
launchctl/open; other platforms usedisclaim::Command - Deterministic shutdown: 750ms eviction deadline prevents overlay lingering; global quit flag stops respawn loops
- Developer controls:
OPENLOGI_DEV_AGENTenvironment 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →