Soduto is a KDE Connect compatible application for macOS. It allows better integration between your phones, desktops and tablets. For more information take a look at soduto.com
- Installation
- Features
- Building
- Debugging
- Verifying Downloads
- Limitations
- Workarounds
- Troubleshooting
- Documentation & Resources
- Get in touch
- Star History
- FAQ
- License
To install builds of Soduto from this repository, head to the Releases Page and download the .dmg file. As an optional step, you may head over to the Verifying Downloads section if you wish to verify its integrity and cryptographic signature. Open it and you might get a pop-up window containing the license. Tap agree if you've read it and wish to continue. Then drag Soduto.app onto the Applications Folder. When running for the first time, you might get a prompt saying that macOS can't run apps from an unidentified developer. Pick any one of the following options to continue:
- Go to
System Preferences → Privacy & Securityand findSodutowas blocked message, then tapOpen Anywayand follow the on-screen instructions. - Remove the quarantine flag from the downloaded
.dmgfile before opening it:xattr -d com.apple.quarantine <path to your dmg directory>/Soduto.Nightly.dmg
- On macOS (14) Sonoma and earlier, you can press and hold
controland click on the app icon. While still holdingControl, selectOpen.
The official build of Soduto can be downloaded from soduto.com or from Soduto's official repository. You may find support for the official build in Soduto's official repository, so only post issues here related to the builds of this repository.
Please note that currently there's no brew cask for Soduto builds of this repository and the only source is the Releases Page of this repository. Installation is a one-time process, and future upgrades are handled in-app using Sparkle.
Soduto implements the following KDE Connect plugin features (compatible with any KDE Connect device):
- Receive — Mirror remote device notifications on macOS (with automatic OTP copying)
- Send — Send macOS notifications to remote devices
- Receive — Receive clipboard content from remote devices
- Send — Send macOS clipboard content to remote devices
- Receive — Receive shared files, links, and text from remote devices (files are saved in the Downloads folder)
- Send — Share files, links, and text to remote devices via Share extension, drag and drop to menu bar, or
Send Filesoption - Browse storage — Access remote device filesystem via SFTP
- Receive — View remote device battery level and charging status
- Send — Send macOS battery status to remote devices
- Receive — Control remote device music/video playback from macOS Control Center and Now Playing Module (experimental)
- Send — Control macOS music/video playback from remote devices
- Receive — View incoming call and SMS notifications from remote devices
- Receive — View SMS of remote devices
- Send — Send SMS from macOS
- Receive — Use remote device as keyboard and touchpad for macOS
- Send — Use macOS to control remote device input (experimental)
- Receive — Receive ping messages from remote devices
- Send — Send ping messages to remote devices
- Receive — Execute macOS commands from remote devices
- Send — Execute predefined commands on remote devices
- Receive — Make macOS play an alarm sound to locate it
- Send — Make remote device play an alarm sound to locate it
- Receive — Monitor remote device network connectivity status
- Receive — Use remote device as presentation remote for macOS
- Send — Use macOS as presentation remote for remote device
- Receive — Synchronize contacts between macOS and remote devices
- Screensaver Inhibit — Prevent macOS screensaver when device is connected
- Call Pause — Automatically pause macOS media during device calls
For the complete list of KDE Connect features and documentation, visit the official KDE Connect Wiki.
-
Clone this repo:
git clone git@github.com:sannidhyaroy/Soduto.git Soduto && cd Soduto
Clone using HTTPS? 👀
Run this command, instead of the above one:
git clone https://github.com/sannidhyaroy/Soduto.git Soduto && cd Soduto
-
Open project
Soduto.xcodeprojwith Xcode (select the Soduto Project menu in Xcode Project Navigator).- Swift Package dependencies (
swift-certificates,SwiftNIO,NIOSSL,NIOSSH,Citadel,Sparkle, etc.) will resolve automatically on first build.
- Swift Package dependencies (
-
Select
Sodutoas Target. Go toSigning & Capabilitiesand under theSigningsection, ensure your appropriateTeamis selected. -
Make sure you have the same
App Group keyforSoduto Shareand also verify that the sameTeamis selected for each target. -
Build target
Soduto
Soduto uses Apple's Unified Logging system (os.Logger). Logs are organized by subsystem and categories:
| Target | Subsystem | Categories |
|---|---|---|
| Soduto | com.soduto.Soduto |
general, network, device, config, services, ui |
| Soduto Files | com.soduto.Soduto-Files |
general, filesystem, ui |
| Soduto Share | com.soduto.Soduto.Soduto-Share |
general, sharing, ui |
# Show all messages (debug, info, notice, error) for Soduto
log stream --predicate 'subsystem == "com.soduto.Soduto"' --level debug
# Show all messages for Soduto Files
log stream --predicate 'subsystem == "com.soduto.Soduto-Files"' --level debug
# Show all messages for Soduto Share
log stream --predicate 'subsystem == "com.soduto.Soduto.Soduto-Share"' --level debug
# Show all messages from ALL Soduto components
log stream --predicate 'subsystem BEGINSWITH "com.soduto.Soduto"' --level debug
# Show only info and above (excludes debug logs) for Soduto
log stream --predicate 'subsystem == "com.soduto.Soduto"' --level infoThe --level debug flag shows messages at debug level and above (info, notice, error, fault).
By default, debug messages are only kept in memory and not saved to disk. To persist them:
# Enable debug persistence for main app
sudo log config --subsystem com.soduto.Soduto --mode level:debug
# Enable debug persistence for file browser
sudo log config --subsystem com.soduto.Soduto-Files --mode level:debug
# Enable debug persistence for share extension
sudo log config --subsystem com.soduto.Soduto.Soduto-Share --mode level:debugThis allows you to view debug messages in Console.app after they occur. To revert to default behavior:
# Revert main app
sudo log config --subsystem com.soduto.Soduto --mode level:default
# Revert file browser
sudo log config --subsystem com.soduto.Soduto-Files --mode level:default
# Revert share extension
sudo log config --subsystem com.soduto.Soduto.Soduto-Share --mode level:default- Open
Console.app - In the search field, enter the subsystem for the target you want to view:
subsystem:com.soduto.Soduto- Main appsubsystem:com.soduto.Soduto-Files- File browsersubsystem:com.soduto.Soduto.Soduto-Share- Share extensionsubsystem BEGINSWITH com.soduto.Soduto- All components
- Filter by category for targeted debugging:
category:network,category:services,category:device(main app)category:filesystem(file browser)category:sharing(share extension)
- Enable "Include Debug Messages" in the Action menu to see debug-level logs
Warning: Debug logging may include sensitive data (clipboard contents, passwords). Only enable its persistence during debugging and revert to default when done.
All releases published onwards v2.0.0 in this repository are cryptographically signed.
This allows you to verify that a downloaded release was published by the Soduto project and was not modified after publication.
This step is optional and intended for users who want additional security guarantees.
- The signing key is not stored in this repository.
- The canonical source of truth for the signing key is the project domain.
- The same key is discoverable automatically via standard OpenPGP mechanisms.
- The DMG itself is not signed with GPG. Instead, the checksum file is signed to verify authenticity and integrity.
- Sparkle handles update security separately.
- Most users do not need to perform these steps, unless they wish to manually verify downloads.
Signed by: Soduto Releases (Soduto Release Signing Key) releases@soduto.thenoton.com
Key fingerprint: 4951 D786 1266 F77E A86D 2B3C D952 D26C 5D6D 9D22
You can use the fingerprint above to manually verify that you have obtained the correct public key.
-
The easiest way is to let GPG locate the key automatically.
gpg --locate-keys releases@soduto.thenoton.com
Not Working? 🥲
- Force Web Key Directory (WKD) (recommended):
gpg --locate-keys --auto-key-locate wkd releases@soduto.thenoton.com
- Force a public keyserver lookup (email-verified at keys.openpgp.org):
gpg --locate-keys --keyserver hkps://keys.openpgp.org releases@soduto.thenoton.com
- Final fallback - manual import:
curl -o soduto-release-signing-key.asc https://raw.githubusercontent.com/sannidhyaroy/soduto-releases/refs/heads/main/.well-known/openpgpkey/hu/i4cdqgcarfjdjnba6y4jnf498asg8c6p gpg --import soduto-release-signing-key.asc
- Force Web Key Directory (WKD) (recommended):
-
Download the
.dmgfile, the.dmg.sha256file and the.dmg.sha256.ascfile. -
Open the directory where you downloaded the
.dmgfile, and run the following commands:gpg --verify Soduto-X.Y.Z.dmg.sha256.asc # Enter correct path to the .dmg.sha256.asc file shasum -a 256 -c Soduto-X.Y.Z.dmg.sha256 # Enter correct path to the .dmg.sha256 file
Expected output includes:
Good signature from "Soduto Releases <releases@soduto.thenoton.com>"This confirms that:
- The checksum file (
.dmg.sha256) was signed by the Soduto Release Signing Key. - The downloaded
.dmgmatches the published SHA-256 checksum.
- The checksum file (
Google introduced privacy restrictions on Android 10 and higher that prevent apps from accessing clipboard data unless the app is the default input method editor (IME) or is currently in focus. This affects seamless clipboard sync between KDE Connect and Soduto. Your clipboard will automatically sync to your other devices when you copy something on your mac, however you will have to manually tap on Send Clipboard in the KDE Connect app every time you want to sync your Android's clipboard to your mac.
On Android 11 and higher, you may not be able to add the root location of your Internal Storage or your Download folder to KDE Connect's Filesystem expose locations due to Google's privacy changes.
On Android 15 and higher, the system hides sensitive notification content (such as passwords, OTPs, and other sensitive information) from applications by default. This means Soduto will display "Sensitive notification content hidden" instead of the actual content. This restriction also affects automatic OTP copying in Soduto. KDE Connect will not receive sensitive notifications unless you explicitly grant the RECEIVE_SENSITIVE_NOTIFICATIONS permission (see workarounds below).
If you have Riru or Zygisk, you can bypass the clipboard restriction on Android 10 or higher by using Kr328's Clipboard Whitelist module and then tick KDE Connect/Zorin Connect from the Clipboard Whitelist app. If you're on Android 13 and the module isn't working for you, try Xposed Clipboard Whitelist (remember to select System Framework for the module scope). You need to have Xposed Framework for the Xposed Clipboard Whitelist module to work.
NoStorageRestrict is an Xposed Module that removes the restriction when selecting folders(like Internal Storage, Android, Download, data, obb) through file manager on Android 11 and higher. There is a Magisk module for this as well but I haven't tested the Magisk Module version yet, so use it at your own risk
The recommended approach is to grant KDE Connect the RECEIVE_SENSITIVE_NOTIFICATIONS permission using ADB (Android Debug Bridge). This will allow sensitive notifications to be delivered normally and enable automatic OTP copying.
-
Set up ADB — Follow the official Android ADB setup guide.
-
Connect your device — Connect your phone via USB or wireless ADB.
-
Grant the permission — Run this command:
adb shell cmd appops set --user 0 org.kde.kdeconnect_tp RECEIVE_SENSITIVE_NOTIFICATIONS allow -
If permission monitoring error occurs — If you see this error, follow these steps:
java.lang.SecurityException: uid 2000 does not have android.permission.MANAGE_APP_OPS_MODES- Go to Developer Options and enable "Disable permission monitoring"
- Reboot your phone
- Run the ADB command again
-
Reboot — Reboot your phone for the changes to take effect.
After completing these steps, sensitive notifications (including OTPs) will be delivered to Soduto, and automatic OTP copying will work as expected.
Go to System Preferences → General → Login Items & Extensions → Sharing info button, and ensure Soduto Share toggle is enabled. If you still have issues, keep reading.
On macOS (14) Sonoma and earlier?
Go to System Preferences → Privacy & Security → Extensions → Sharing, and ensure Soduto Share is checked. If you still have issues, keep reading.
If Soduto Share doesn't appear in the share menu, only when multiple files are selected, macOS may have cached a stale extension registration. To fix this:
- Check for stale plugin registrations:
pluginkit -m -Dv -i com.soduto.Soduto.Soduto-Share
- If multiple entries appear (or entries pointing to non-existent paths), remove them:
pluginkit -r "<path shown in the output>" - Restart your Mac, then open Soduto. The extension should re-register automatically with the correct rules.
If you see SSL/TLS certificate errors in Console.app after re-pairing your device, the remote device is rejecting Soduto's certificate.
Connection error: NIOSSL.NIOSSLError.handshakeFailed(NIOSSL.BoringSSLError.sslError([Error: 268436502 error:10000416:SSL routines:OPENSSL_internal:SSLV3_ALERT_CERTIFICATE_UNKNOWN at /Users/sannidhyaroy/Library/Developer/Xcode/DerivedData/Soduto-djazogzlahmuayfoqwqoflmgulsi/SourcePackages/checkouts/swift-nio-ssl/Sources/CNIOBoringSSL/ssl/tls_record.cc:484]))
This SSLV3_ALERT_CERTIFICATE_UNKNOWN error means the remote device is rejecting Soduto's certificate as "unknown"
Click here for Legacy versions (CocoaAsyncSocket)
socketDidDisconnect(<<GCDAsyncSocket: 0x...>> withError:<Error Domain=kCFStreamErrorDomainSSL Code=-9829 "(null)" UserInfo={NSLocalizedRecoverySuggestion=Error code definition can be found in Apple's SecureTransport.h}>)
This is error code -9829 (errSSLPeerCertUnknown), which means the remote device is rejecting Soduto's certificate as "unknown"
When does this happen?
This occurs when Soduto's keychain entries are cleared (manually or by reinstalling) but the app's preferences remain. Soduto generates a new SSL certificate with the same device ID but different cryptographic content. The remote KDE Connect app has cached the old certificate and rejects the new one as a mismatch.
Symptoms:
- Device pairing appears to succeed
- Main connection works initially
- Secondary connections fail (file transfers, notification icons, etc.)
- The paired device may disconnect and fail to reconnect after restarting Soduto
Note: With the current SwiftNIO implementation, previously paired devices usually reconnect successfully despite this error appearing in logs. However, devices that were paired before the certificate was regenerated and never re-paired will not connect.
Why does this happen?
Soduto and KDE Connect use SSL/TLS with self-signed certificates to secure communications. Each device stores the other's certificate during pairing. KDE Connect on Android caches certificate data aggressively, and this cache persists even after unpairing. When Soduto presents a new certificate (same device ID, different key), KDE Connect's cached data causes validation to fail.
Solution:
- Unpair the device from both Soduto (on Mac) and KDE Connect (on Android/Linux)
- On Android: Go to
Settings → Apps → KDE Connect → Storage → Clear Cache- Note: Clear cache only, not app data (clearing app data will remove all your KDE Connect settings)
- On Mac (optional): Remove Soduto's preferences to get a fresh device ID:
Caution
Running the following command is optional and dangerous as it will delete your saved Soduto Preferences. Only run when you absolutely want a fresh device ID and are fine with reconfiguring lost preferences
defaults delete com.soduto.Soduto- Re-pair the devices
After clearing the cache, the new certificate will be accepted and cached correctly.
For comprehensive information about KDE Connect features, limitations, configuration, and troubleshooting across all platforms, visit the official KDE Connect Wiki. While the wiki primarily focuses on Linux environments, a lot of the information will still be useful for macOS and Android.
To ask a question, offer suggestions or share an idea, please use the discussions tab of this repository.
If you spot any bugs or vulnerabilities, please create an issue. It's always a good idea to make sure there aren't any similar issues open, before creating a new one!
This project only exists because of all the people who have contributed.
No, this is not the official version of Soduto. Head over to their official site or GitHub Repo for the official version.
The development for the official version seems to have been inactive for a very long time. Thus, a lot of Soduto's features were broken on recent versions of macOS. This is why, I have tried to replace some deprecated code in my repo.
The code is public, so instead of taking someone else's word for it, it's better to review it yourself if the app is safe.
The reason macOS shows a prompt saying that this app is from an unidentified developer is because I have a free Apple Developer Account and not a paid one, thus the builds of Soduto released by me are not notarized. If you build the app yourself for your own mac, you won’t get the warning. Head over to the building section to do so.
Developers can't send an app for notarization with a free Apple Developer Account. I am a student and developing apps for the Apple Platform is neither my job nor my hobby. Neither can I nor do I want to pay Apple a hefty amount of $99 every year for the privilege of developing apps for their platform.
Same as above. I also don't want to have to go through their app review process.
Soduto is licensed under the GNU General Public License v3.0.