Inspiration

Every mainstream way of sending a large file is really two transfers with a wait in the middle: you upload it to somebody else's computer, and then the other person downloads it back out. That detour costs time, it leaves a copy of your file on a server you do not control, and a quota you did not choose decides how big your file is allowed to be.

AirDrop showed what it feels like when the middle is gone — but only inside one ecosystem and one room. We wanted that feeling across Windows, iOS and Android, and across the internet.

What it does

Aetherly moves your files peer-to-peer, straight from one device to the other. A file streams over an end-to-end encrypted WebRTC channel from the sending device to the receiving one. It is never uploaded, never parked, and never held by us — not even for a second. Our server does only what a server has to do: sign you in, hold your contacts and permissions, tell you who is online, and introduce the two devices to each other. It never sees a byte of what you send.

Aetherly is live on the App Store, on Google Play and as a signed Windows app — one account across all three.

  • Send anything. A single file, a whole folder tree, a text snippet, or a screenshot straight out of your clipboard. No size limit on the top plan.
  • Your own devices feel like one machine. Sends between your own devices auto-accept; a text snippet can land directly in the clipboard on the other machine, ready to paste.
  • Remote accept and pull from history (Pro). Accept an incoming transfer from your phone and have it land on your desktop, or pull a file out of another device's history onto the one in your hand.
  • Contacts, favorites and opt-in presence. Pin the people you send to most. You decide when you are visible.
  • Cross-device history that stores metadata only — a name and a size, never the file. One tap re-sends a failed transfer to the same person.
  • Honest delivery states. The receiver acknowledges only after it has written and verified the files. Every history row is delivered, partial, failed or unconfirmed.
  • No surprises on arrival. Every platform checks free disk space before accepting, and refuses before a single byte flows.
  • Windows power features. Right-click any file in Explorer and "Send with Aetherly" without opening the app; a staging area gathers several files into one send; the app lives in the system tray; a custom notification overlay surfaces incoming transfers even inside a remote desktop session; and it runs fully headless on a machine with no screen.
  • iOS: a Share Extension sends from inside any app, with push notifications, Live Activity progress and background transfer protection.
  • Android: share-sheet send, QR pairing and foreground-service transfers that survive the screen going off.
  • Four languages. English, German, French and Spanish — in the apps, in emails and in push notifications.

Built in Germany under GDPR by DuyTec GmbH. Free to start, with paid plans that raise the device count, size ceiling, files per transfer, favorites and concurrent transfers.

How we built it

Three native clients, one server, one contract.

  • iOS: Swift + SwiftUI, WebRTC via stasel/WebRTC, a Share Extension, a Notification Service Extension and a widget extension, RevenueCat for subscriptions.
  • Android: Kotlin + Jetpack Compose, Hilt, Ktor, stream-webrtc-android, RevenueCat on Google Play Billing.
  • Windows: Electron + React + TypeScript, node-datachannel for WebRTC, an Authenticode-signed installer with its own update feed.
  • Server: Node.js + Fastify + SQLite (Drizzle), handling auth, contacts, presence fan-out and WebRTC signaling only. STUN-only — there is no relay, so bytes never touch our infrastructure.
  • A shared TypeScript package is the single source of truth for the wire protocol and API contracts; the Swift and Kotlin implementations transcribe it, and cross-platform parity tests fail the build when the three drift apart.

How we use RevenueCat

RevenueCat is how Aetherly sells and enforces subscriptions on iOS and Android.

  • What is sold: two paid tiers, Basic and Pro, each monthly and yearly. The paywalls are native — SwiftUI on iOS, Jetpack Compose on Android — and read their packages from a RevenueCat Offering, so tier layout, prices and the monthly/yearly toggle are configuration, not a build.
  • Purchases, restores, upgrades and downgrades go through the RevenueCat SDK on both stores, including the receipt validation we would otherwise have to build and keep correct twice, once per store.
  • Entitlements are synced to our own server, not trusted from the device. A RevenueCat webhook posts every subscription event to our backend, which resolves the product to a plan, records which store it came from, and writes it onto the user record. Every limit the app enforces is read back from that server-side plan.
  • That sync makes the plan cross-platform. A subscription bought on iPhone or Android raises the same account's limits on Windows too, because all three clients ask the same server what the account is entitled to. Desktop purchases run through Stripe, and both paths converge on one plan record — a customer has one plan, not one per platform.
  • A genuinely free tier exists, so the funnel is real: people use the product first, hit a device limit or a file-size ceiling, and see the paywall in context rather than at launch.

Challenges we ran into

  • Three WebRTC stacks that must interoperate byte-for-byte. node-datachannel, stasel/WebRTC and stream-webrtc-android each have their own flow-control behaviour; chunk sizes, buffer thresholds and watermarks are pinned by a parity test across all three codebases.
  • Receipts, not hope. Making "sent" mean "arrived" required an acknowledgement over the DataChannel and over the server socket (which outlives the P2P channel), a fourth unconfirmed state for old clients and dropped connections, and a rule that waiting never blocks the sender.
  • Background limits on mobile. iOS suspends, Android kills; getting transfers to survive the screen going off on both took foreground services, background protection windows and careful cleanup on interruption.
  • Four languages without shared code. Swift, Kotlin and TypeScript each carry their own translation catalogs; key parity, German register and "not accidentally still English" are build gates on every platform.
  • Privacy as architecture. A real profile name is only revealed once a contact relationship is active on both sides — on every path, including the push title.

Accomplishments that we're proud of

  • Shipped the first public App Store and Google Play releases in September 2026, next to a signed Windows client.
  • Files never touch a server, and there is no size limit on the top plan — the core promise held on all three platforms.
  • One design system in three native implementations (SwiftUI, Compose, React), with WCAG contrast ratios enforced by a test that fails the build.
  • A Windows client that behaves like a capability the OS grew, not a ported web app: Explorer context menu, tray, clipboard paste, RDP-visible notifications, headless mode.
  • One plan across every platform, with RevenueCat on both mobile stores and Stripe on the desktop converging on a single server-side entitlement.

What we learned

That the hardest part of "no server in between" is everything the server used to hide: delivery confirmation, presence and the backpressure. And that monetization works best when the free tier is a real product and the paywall appears at the exact moment a limit is reached, not at launch.

What's next for Aetherly

  • Sign in with Apple and Google on every platform (in progress, already running on our beta server).
  • macOS and Linux desktop clients on the same P2P engine.
  • Bringing local-network discovery back in a privacy-preserving form.
  • A managed TURN fallback for users behind symmetric NAT / CGNAT.

Built With

Share this project:

Updates

Submission history