How to Start a Rust App on Login in macOS
A deep dive into the challenges of starting Rust apps on login in macOS, comparing Tauri's autostart plugin with a native approach using smappservice-rs.


I'm on X/Twitter at@iparaskev
Contents#
- Introduction
- Using Tauri Autostart Plugin for Rust Apps on macOS
- Native macOS Login Items with smappservice-rs in Rust
- Conclusion
Introduction#
At Hopp, our goal is to make the remote pair programming experience as seamless as possible. For that, our desktop app needs to be ready the moment you want to hop on a code pairing call—so it needs to start automatically when the user logs in.
Hopp is built with Tauri, so the natural step was to use Tauri's autostart plugin. While it worked as expected, we noticed a few limitations that didn't fit our vision for a polished, near-native user experience.
That's why we built smappservice-rs
:
a Rust wrapper for macOS's ServiceManagement
framework.
In this post, I'll share why we made this decision, what you get with our library, and how it helps us deliver a better experience for Hopp macOS users.
Using Tauri Autostart Plugin for Rust Apps on macOS#
Tauri's autostart plugin works across Windows, macOS, and Linux, allowing your app to start automatically at system startup. Under the hood, it uses the auto-launch crate, which is the Rust equivalent of node-auto-launch.
On macOS, the plugin supports two methods for starting your app on login:
- AppleScript
- LaunchAgent plist file
AppleScript
The AppleScript method executes a script that instructs System Events to add or remove a login item for your app. When this method is used, the user will see two pop-ups:
- A permission request for access to System Events.
System events permission popup - A notification that the app was added to the login items by System Events.
System events notification
We see two issues with this approach:
- It requires two pop-ups, instead of just one when using a native API.
- It isn't entirely clear from the pop-ups which app is being added to the login items.
Ditch the frustrating "Can you see my screen?" dance.
Code side-by-side, remotely.
LaunchAgent
This method adds a plist file to the user's Library/LaunchAgents
directory. When this method is used,
the user receives a notification.

While there is only one notification, the same issue from the AppleScript method remains: the notification does not specify which app was added to the login items. (This is configurable and was added recently in the implementation.)
Another issue with the LaunchAgent method is that the user has no straightforward way to remove the app from starting on login. The only option is to manually delete the plist file. Even if the app is uninstalled, the entry may remain in the "Allow in the Background" list.
While both methods successfully start your app when the user logs in, they fall short of the seamless, native experience we want to deliver with Hopp.
Native macOS Login Items with smappservice-rs in Rust#
smappservice-rs
is a Rust wrapper around the macOS ServiceManagement
framework. The ServiceManagement
framework in macOS provides a way for applications to manage system services. With it, you can:
- Register your application as a login item
- Register and manage launch agents and daemons from the application bundle
- Check the status of registered services
The main advantage of using smappservice-rs
instead of auto-launch
is that it leverages the native
macOS API to manage login items, resulting in a more polished and integrated user experience.
For example, when you use smappservice-rs
to register your app as a login item, the user will see a single
notification that states which app was added to the login items.

Another benefit is that if a launch agent is registered, it isn't installed in ~/Library/LaunchAgents
,
but instead comes from the app bundle. This means it'll be removed automatically when the app is
uninstalled, keeping the system clean and user-friendly.
How to register a Rust app as a login item with smappservice-rs#
To register your Rust app as a login item using smappservice-rs
, you need to
use smappservice_rs::{AppService, ServiceType};
let app_service = AppService::new(ServiceType::MainApp);
match app_service.register() {
Ok(()) => println!("App registered successfully!"),
Err(e) => eprintln!("Failed to register app: {}", e),
}
You can also unregister the app as a login item with:
use smappservice_rs::{AppService, ServiceType};
let app_service = AppService::new(ServiceType::MainApp);
match app_service.unregister() {
Ok(()) => println!("App unregistered successfully!"),
Err(e) => eprintln!("Failed to unregister app: {}", e),
}
Finally you can check if the app is registered as a login item with:
use smappservice_rs::{AppService, ServiceType, ServiceStatus};
let app_service = AppService::new(ServiceType::MainApp);
let status = app_service.status();
if status == ServiceStatus::RequiresApproval {
AppService::open_system_settings_login_items();
}
You can read more about the features and usage of smappservice-rs
in the
documentation.
Ditch the frustrating "Can you see my screen?" dance.
Code side-by-side, remotely.
Conclusion#
Choosing the right approach for starting a Rust app on login is essential for delivering a native and seamless user experience on macOS. While Tauri's autostart plugin offers a cross-platform solution, its macOS implementation comes with trade-offs—such as less clear notifications and limited user control—that didn't align with our goals for Hopp.
By developing smappservice-rs
, we were able to leverage the native ServiceManagement
framework, resulting
in a more integrated and user-friendly experience. This approach ensures that login items are managed
transparently and cleanly, both for users and the system.
If you have any questions or think we missed something, feel free to reach out on X/Twitter or email me at iason@gethopp.app.