Skip to content
carlonzoPublic

Latest commit

 

History

884 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Klardrop

Klardrop

Cross-platform, peer-to-peer nearby sharing for Android, iOS, macOS, and desktop.

Latest release License: Apache 2.0 Status: public beta


What is Klardrop?

Klardrop is an open-source nearby-sharing app — think AirDrop, but it works between every device you own regardless of the operating system. Pick a file or a snippet of text on one device, choose a nearby device, and it lands on the other side a moment later. There is no account, no cloud, no upload step: bytes go directly from one device to the other over the local network (or Bluetooth when Wi-Fi can't reach).

It's built with Kotlin Multiplatform and Compose Multiplatform, so the discovery, transfer, and UI code is shared across every target.

Supported platforms

Platform Status Notes
Android ✅ Full app with a system share-sheet extension
iOS ✅ Full app with a share extension
macOS ✅ Native app with a menubar presence
Desktop (JVM) ✅ Windows / Linux / macOS via the JVM build

What you can send

  • Files of any type and size (streamed in chunks, with progress)
  • Text snippets and links
  • Clipboard contents (between paired devices)

How it works

Klardrop has three jobs: find nearby devices, trust them once, and move bytes between them.

1. Discovery — finding devices nearby

Klardrop simultaneously advertises itself and scans for other Klardrop instances on every transport it has available:

  • mDNS over Wi-Fi for devices on the same local network (service type _klardrop._tcp.). It can also discover devices speaking Google's Nearby Share protocol (_FC9F5ED42C8A._tcp.) for cross-app interop.
  • Bluetooth Low Energy as a fallback medium when Wi-Fi isn't reachable. Both devices broadcast a well-known service UUID and exchange short device IDs in the scan-response so they can recognise each other without connecting first.

Every discovered device shows up in a live list with its name, OS, and device type. The friendly name and metadata are only revealed after an authenticated handshake, never over open airwaves.

2. Pairing — establishing trust

The first time two devices talk to each other, they go through a one-time pairing flow that you confirm with a tap on both ends. Under the hood:

  • Each side generates a fresh ECDH (P-256) key pair for the session and exchanges the public halves.
  • Each side derives the same shared secret locally via ECDH — the secret itself never crosses the wire.
  • Each side also presents its long-lived ECDSA (P-256) identity public key so future messages can be verified as coming from the same device.
  • A short verification code derived from both public keys can be compared visually, protecting the pairing step against a man-in-the-middle attacker on the LAN.

Once paired, the shared secret and the peer's identity key are stored locally on both sides and the devices recognise each other automatically forever after.

3. Transfer — moving the bytes

Klardrop uses its own compact wire format: a 4-byte length prefix, a 1-byte message type, and a Protocol Buffer payload. The same format flows over every transport (Wi-Fi TCP, Nearby Share TCP, BLE GATT), so the rest of the system doesn't have to care which medium the bytes travelled over.

For files, the sender announces the file metadata, the receiver acknowledges (ACK_READY), and the payload is streamed in 32 KB chunks with live progress on both ends. Every chunk carries an authentication tag (see below), and the whole transfer is acknowledged when complete (ACK_RECEIVED). If a connection drops, the sender retries with exponential back-off and rebuilds the connection from scratch.

The wire format, message types, and ACK state machine are documented in detail in KLARDROP_PROTOCOL.md.

Security

Klardrop is built to feel like dropping a file across the room — and the security model is designed around that mental image: bytes only move between your devices after you've explicitly approved that they know each other, and they only ever travel directly between those two devices.

  • Peer-to-peer, no server in the middle. Klardrop has no backend. Transfers go directly from one device to the other over your local Wi-Fi or a direct BLE link. Your files never touch our infrastructure — there is no infrastructure.
  • Explicit pairing with confirmed identity. Two devices can't talk to each other in earnest until you've paired them via an on-device prompt on both ends. Pairing performs an ECDH (P-256) key exchange that gives both sides a shared secret derived locally — the secret itself is never transmitted — and binds it to each device's ECDSA (P-256) identity key. From that point on, only your paired devices can impersonate or be impersonated by each other.
  • End-to-end message authentication. Every message exchanged between paired devices is wrapped in a TrustedMessage envelope carrying an ECDSA signature, a timestamp, and a random nonce. The receiver verifies the signature against the pinned identity key, rejects stale timestamps, and rejects nonces it has already seen — so tampered, replayed, or spoofed messages are dropped before they reach the app.
  • Per-chunk file integrity. Large file transfers are protected chunk-by-chunk with HMAC-SHA256 tags keyed by a per-pair key derived from the ECDH shared secret via HKDF-SHA256. The MAC binds each chunk to its position in the transfer, so an attacker can't reorder, splice, or replay chunks between transfers — any mismatch fails the entire transfer immediately.
  • Identity keys live in the platform-secure store. On every platform, your device's long-lived private signing key is held by the OS keystore — Android Keystore, iOS / macOS Keychain, and the OS-secure store on desktop — so it never leaves the device and never appears in app storage in plaintext.
  • Minimal exposure on the wire before pairing. BLE advertisements carry only a random 8-character device id and the service UUID — your device name, OS, and device type are only revealed after the authenticated Klardrop handshake.

A note on transport encryption. Klardrop's threat model is "trusted devices on a network you control" and the current focus is authenticated integrity rather than confidential transport: messages between paired devices are signed and file chunks are MAC'd, but payloads themselves are not yet symmetrically encrypted on the wire. Adding an authenticated-encryption layer (AES-GCM / ChaCha20-Poly1305) keyed from the same ECDH secret is on the roadmap; until then, prefer running Klardrop on networks you trust (your home / work Wi-Fi, your hotspot, or BLE), rather than on shared open Wi-Fi.

If you find a security issue, please follow the private reporting instructions in SECURITY.md rather than posting a public proof-of-concept.

Install

Binaries are published to GitHub Releases on every tagged version.

Platform Format Notes
macOS .dmg Unsigned during beta — run xattr -dr com.apple.quarantine /Applications/Klardrop.app after first launch if Gatekeeper blocks it
Windows .msi Unsigned during beta — SmartScreen will warn on first run
Linux Script / AUR Native GUI via install.sh (Qt desktop GUI by default, Omarchy shell integration on Omarchy; no JVM required); AUR klardrop-bin remains legacy JVM (JRE 21+)
Android .apk Sideload; Play Store internal-testing track planned
iOS TestFlight Public invite link forthcoming

On Linux and macOS the native build is two binaries with two jobs, and they are never mixed:

Command What it is
klardrop Native CLI client — the one you type (klardrop send, klardrop status, …)
klardrop-engine The daemon, started by systemctl --user start klardrop.service (the klardrop.service user unit)

The Linux install script puts both in ~/.local/bin, and the in-app self-updater replaces the pair together — a CLI from one release is never left beside an engine from another. The AUR packages follow the same split: klardrop-native-bin owns both binaries and the systemd unit, klardrop-qt-bin depends on it and adds only the Qt frontend, and klardrop-omarchy adds the shell integration.

The desktop app checks for updates on launch via the latest.json manifest published alongside each release.

Repository layout

Module Purpose
common/ Shared business logic: discovery, transport, file management, trust, DI
compose-ui/ Shared Compose Multiplatform UI (Android + desktop JVM)
control-plane/ Shared authenticated loopback control API (devices, share, transfers, …) that every host serves: the Linux engine, the JVM desktop app and the native macOS app
protos/ Protocol Buffer definitions for the wire format and Nearby Share interop
android/ Android app + share extension
iosApp/ iOS app + share extension, and the native macOS app (Xcode targets iosApp and KlardropMac; the macOS app is presentation/src/macosMain + iosApp/iosApp/App/MacApp.swift, there is no separate macos/ module)
desktop/ JVM desktop app (Windows / Linux / macOS)
cli/ Kotlin/Native daemon engine, shipped as klardrop-engine (see cli/README.md)
cli-rust/ Native Rust CLI client, shipped as klardrop (see cli-rust/README.md)

Building and running

Requires JDK 21. For mobile / native targets you also need the platform toolchain (Xcode for iOS and macOS, the Android SDK for Android). Everything else — Kotlin, Compose, Ktor, jmDNS, dbus-java, the cryptography library, the protobuf generator — is pulled in by Gradle, so there's no separate setup step.

# Desktop (JVM) on the current host
./gradlew :desktop:run
# or, equivalently:
./run-desktop.command

# Android (install on a connected device or emulator)
./gradlew :android:installDebug

# Command-line client
./gradlew :cli:run --args="--help"

# macOS / iOS — open in Xcode
open iosApp/iosApp.xcworkspace

# Tests
./gradlew test

Contributing

Klardrop is a personal project: the codebase is being tidied in the open. Issues and pull requests are welcome, especially:

  • Bug reports with reproducible steps
  • Platform-specific fixes (BLE quirks, mDNS edge cases, share-extension oddities)
  • Security review of the trust / pairing flow
  • UI polish on the shared Compose screens

Acknowledgements

Huge thanks to @grishka and his project NearDrop. Klardrop's Nearby Share / Quick Share support — and the ability to send to and receive from Android — is only possible because grishka reverse-engineered Google's protocol and wrote it up in PROTOCOL.md. The Kotlin port that lives in this repo started as a translation of his Swift implementation; the wire format, the UKEY2 handshake glue, the paired-key dance, and the payload-layer framing all follow his lead. Thank you for the work and for sharing it openly.

License

Apache License 2.0 © 2022–2026 Carlo Marinangeli.

Releases

Contributors

Languages