10 Novembre

RPP: A Transparent Python Loader Written in Rust - Beyond PyInstaller's Limitations

When Distribution Challenges Lead to Innovation: The Story of RPP

I recently faced an interesting challenge with my Python application AutoCorrect. What started as a simple distribution problem turned into an opportunity to both solve a common issue and strengthen my Rust programming skills.

AutoCorrect: Where It All Started

AutoCorrect started as a side project to streamline my daily writing tasks. It’s an AI-powered text processing tool that leverages multiple AI models (OpenAI, Anthropic’s Claude, and Google’s Gemini) for instant text correction, translation, and reformulation. The application prioritizes privacy - no data collection, just direct API communication - and offers complete control through user-provided API keys. What makes it particularly useful is its ability to process text in less than a second while maintaining a simple, intuitive interface.

The Python Distribution Challenge

The PyInstaller Dilemma

When distributing Python applications to end users, PyInstaller has long been the go-to solution. It packages everything - your code, dependencies, and Python interpreter - into a single executable. However, this approach comes with a significant drawback: antivirus detection.

PyInstaller Detection

As shown above, my legitimate AutoCorrect application was flagged by 19 different antivirus engines when packaged with PyInstaller. This happens because malware authors frequently use PyInstaller to obfuscate malicious code, leading antivirus vendors to implement aggressive detection rules for PyInstaller’s bootloader.

Nuitka Isn’t the Answer

Some developers turn to Nuitka as an alternative, which compiles Python code to standalone executables. However, it faces similar detection issues because it still needs to bundle everything into a single executable, triggering the same antivirus heuristics.

The Birth of RPP: A Different Approach

Instead of fighting against antivirus software, I decided to take a radically different approach. Rather than hiding or packing anything, why not create a completely transparent loader? This idea led to RPP (Rust Python Packager), and I chose Rust for its performance, safety guarantees, and as an opportunity to deepen my knowledge of the language.

How RPP Works

Unlike PyInstaller or Nuitka, RPP doesn’t try to pack or hide anything. It’s a lightweight loader that:

  1. Manages a dedicated Python runtime in the user’s application data directory
  2. Creates isolated virtual environments for each application
  3. Handles updates transparently
  4. Keeps all Python code accessible and inspectable

The result? Only one antivirus detection, likely due to a generic rule about Python execution:

RPP Detection

Real-World Implementation

Here’s RPP in action:

RPP Installation Demo

The loader provides clear feedback during all operations, maintaining complete transparency with users.

Technical Deep Dive

The Loader Architecture

RPP’s architecture is intentionally simple. Instead of complex packing mechanisms, it:

  1. Downloads a compatible Python runtime for the user’s platform
  2. Creates a dedicated virtual environment
  3. Installs the application’s dependencies
  4. Launches the Python application directly

All files remain accessible and inspectable - there’s no obfuscation or packing involved. The Python code is stored in plain text, allowing users or security tools to easily verify its contents.

Cross-Platform Advantage

One of PyInstaller’s limitations is the need to create different packages for each operating system. RPP solves this by handling platform-specific details at runtime:

fn get_runtime_info<'a>(&self, metadata: &'a Metadata) -> (&'a str, PathBuf) {
    if cfg!(windows) {
        (&metadata.windows_runtime, self.paths.base_dir.join("python.tar.gz"))
    } else {
        (&metadata.linux_runtime, self.paths.base_dir.join("python.tar.gz"))
    }
}

This means one loader can handle multiple platforms, simplifying distribution and maintenance.

Update System

The update system is equally transparent. RPP checks a simple metadata endpoint:

const METADATA_URL: &'static str = "https://api.angelkarlsson.eu/autocorrect/loader/multi/v2";

This endpoint provides information about:

  • Latest runtime versions
  • Application updates
  • Platform-specific configurations

No hidden mechanisms or complex protocols - just straightforward HTTPS requests and clear metadata.

Learning Rust Through Practice

Developing RPP was also an excellent opportunity to deepen my understanding of Rust. The project required handling:

  • Async operations for downloads and installations
  • Cross-platform file system operations
  • Error handling across different contexts
  • Progress reporting and user feedback

Installation Experience

For Windows users, I used Inno Setup Compiler to create a familiar installation experience. This approach provides:

  • Standard Windows installer interface
  • Proper installation path selection
  • Automatic uninstallation support

Future of RPP

I’ve launched rpp.fieryaura.eu to showcase the project. While initially created to solve AutoCorrect’s distribution challenges, RPP could help other developers facing similar issues.

The project demonstrates that sometimes the best solution isn’t to fight against security tools but to work with them through transparency. I’m currently considering whether to make RPP open source or keep this experiment private.

Current Limitations and Future Improvements

While RPP effectively solves the antivirus detection issue, it’s still a work in progress. I’m currently developing a checksum verification system to ensure code integrity and prevent third-party modifications. Additionally, incremental updates are on the roadmap to optimize the update process and reduce bandwidth and CPU usage.

Conclusion

RPP shows that complex problems sometimes require simple solutions. By choosing transparency over obfuscation, we can avoid antivirus detection while providing a better experience for both users and developers.

The project also highlights how technical challenges can lead to learning opportunities. What started as a distribution problem became a chance to explore Rust and rethink Python application distribution.


Want to discuss Python distribution challenges or Rust development? Contact me on Linkedin Visit nils.begou.dev or check out the RPP project at rpp.fieryaura.eu.

Mots-cles

Python Rust PyInstaller Nuitka Software Distribution Development Cross-Platform Application Loader