Two-Component Architecture
KVantage employs a two-process architecture that separates GUI functionality from privileged system operations:Frontend: Compose Desktop GUI
- Technology: Kotlin + Compose Multiplatform for Desktop (JVM)
- Privilege level: Runs as normal user (never root)
- Responsibilities:
- User interface rendering and interaction
- Application state management
- Launching and communicating with backend daemon
- Settings persistence and theme management
Backend: kvand Daemon
- Technology: Go
- Privilege level: Requires root access for ACPI operations
- Responsibilities:
- Reading from and writing to
/proc/acpi/call - Executing ACPI commands for Lenovo-specific hardware control
- Responding to frontend commands via simple text protocol
- Reading from and writing to
Communication Protocol
The frontend and backend communicate through stdin/stdout using a line-based text protocol:Command Format
Get Operations (read hardware state):Handshake Protocol
When the frontend launches the backend daemon, it waits for aREADY message before proceeding:
Root Privilege Management
Why Root Access is Required
The/proc/acpi/call interface requires root privileges for both read and write operations. This is a kernel-level interface that provides direct access to ACPI (Advanced Configuration and Power Interface) methods.
Security Model
KVantage implements a privilege separation model to minimize security risks:- GUI never runs as root - The application explicitly checks and prevents execution as root:
- Backend escalates only when needed - The kvand daemon uses
pkexecto request root privileges:
- Single password prompt - The user is prompted for their password once per session when the backend starts, not for every operation.
- Isolated attack surface - Only the small Go daemon runs with elevated privileges, while the entire GUI application runs as a normal user.
ACPI Interface Access
The /proc/acpi/call Interface
KVantage interacts with Lenovo laptop hardware through the acpi_call kernel module, which exposes the /proc/acpi/call pseudo-file.
Write operation (send ACPI command):
ACPI Command Constants
The backend defines Lenovo-specific ACPI paths:Embedded Backend Binary
Due to JVM limitations (executables cannot be run directly from JAR files), KVantage uses a temporary file extraction strategy:- The compiled
kvandbinary is embedded in the JAR at/backend/kvand - On startup, the frontend extracts it to a temporary file
- Sets executable permissions (
chmod +x) - Launches the process
- The temp file is deleted on application exit
Design Rationale
Why Not Run Everything as Root?
Running GUI applications as root is a significant security risk:- Compromised UI code could damage the entire system
- User input handling vulnerabilities become critical
- Accidental file operations have no safety net
Why Not Use a System Service?
While a systemd service would be more traditional, KVantage prioritizes:- Ease of installation - No system configuration required
- Portability - Works across different init systems
- User control - No persistent background process
- Simplicity - Single JAR file deployment
Why Go for the Backend?
From the README:Due to some limitations of the JVM and Kotlin Native, I decided to reimplement batmanager in Golang.Go provides:
- Easy cross-compilation to native binaries
- Small executable size (embeddable in JAR)
- Simple file I/O without JNI complexity
- Straightforward privilege escalation handling
External References
- Backend source code: github.com/kosail/Kvand
- Inspired by: batmanager by LevitatingBusinessMan (Rust CLI tool)
- ACPI call module: Required kernel module for
/proc/acpi/callsupport