AppDrop
Share iOS, Android, and Mac apps with a link. Upload an .ipa, .apk, .aab, .apks,
or a ZIP containing a Mac .app. You can also upload at appdrop.app/drop.
npx appdrop path/to/App.ipa
# https://appdrop.app/d/k3m9x2p7qa
For an iOS build, the recipient opens the link on their iPhone, connects the Tuft app once, signs the app with their own Apple ID, and installs it. Everything happens on the phone; no Mac, cable or provisioning profile with their device in it is needed.
Android and Mac
npx appdrop build/App.apk
npx appdrop build/App.aab
npx appdrop build/App.apks --force
npx appdrop build/MyApp.app # macOS: packages the bundle with ditto
npx appdrop build/MyApp.zip
Android and Mac downloads skip Apple sign-in and iPhone pairing. AppDrop preserves the original signed files and package/bundle identifiers. Unsigned Android builds receive an AppDrop-managed signature, with an encrypted per-link key retained in private storage for future updates.
- APK: open the download on Android and follow its installer. Android may ask you to allow the browser to install apps. Already signed APKs are verified and distributed unchanged. Unsigned APKs are signed automatically. Updates need the same package ID and signing key.
- AAB: AppDrop builds a universal APK and signs it with the link’s managed key.
The recipient downloads a normal
.apk. A universal APK contains only feature modules eligible for fusing; apps that depend on on-demand Play delivery may need a dedicated APK build. - APKS: upload the complete split archive from bundletool or a compatible
exporter. Publishing fails with
apks_requires_forceunless--forceis explicitly supplied. Browser uploads require Distribute APK set anyway. Open it with a split APK installer, such as App Manager. Android’s ordinary browser installer cannot install a split set by itself. AppDrop reads metadata from the base APK, verifies all split signing identities, and preserves already signed archives unchanged. Entirely unsigned sets are signed with the managed key. This does not merge splits into a standalone APK. - Mac: upload a ZIP containing exactly one
.app, or pass the.appto the CLI on macOS. The recipient unzips it and moves it to Applications. Existing Developer ID signing and notarization are retained; AppDrop does not sign, notarize, or bypass Gatekeeper. DMG and PKG uploads are not supported.
Downloading saves the share link to the browser’s app library. A download is not recorded as a confirmed installation. Automatic installation, updates, and renewal remain iOS features.
Builds of the same app
Someone can keep several links to the same app (one bundle ID) in their
library, such as a branch's build beside main's. Their phone holds one at a
time, so AppDrop asks before one replaces another, and only the installed one
updates automatically. To tell them apart, each link shows a label: the git
branch the CLI was run from, or --label:
npx appdrop build/App.ipa --label "onboarding redesign"
Updating an app
Every link also has an update link, https://appdrop.app/d/<id>/manage#<key>.
Keep it private: anyone who has it can publish new builds to the share link, so
the CLI prints only the share link. On the computer that created the link, pass
the share link to --update; the key is saved in ~/.appdrop/drops.json.
npx appdrop links prints the update link, which you can open in a browser to
upload a new build, or pass to the CLI on another computer:
npx appdrop build/App.ipa --update https://appdrop.app/d/k3m9x2p7qa/manage#…
The new file must keep the same platform and bundle/package identifier.
Android uploads must also retain the same signer certificate fingerprints. APK
and AAB uploads can update each other when the final APK uses the same key.
AppDrop verifies the actual files on the server; client-declared signatures are
not trusted. A changed key fails with android_signer_changed, even with
--force. Invalid signatures fail with invalid_android_signature, rather than
being treated as unsigned. Existing links are checked against their published
file before the first update under this policy.
For iOS builds, people who installed an earlier version see "Update available" when they open the share link again,
and one tap re-signs and installs it with the Apple ID their browser
remembers. iOS keeps the app's data.
Apple Vision Pro builds
An iPhone/iPad link can also carry the same app's native Apple Vision Pro
build. Publish the visionOS .ipa to the link's update link:
npx appdrop build/AppVision.ipa --update https://appdrop.app/d/k3m9x2p7qa/manage#…
It must use the same bundle identifier as the link's app (adding visionOS as a destination of the app's target in Xcode keeps it). It doesn't replace the iPhone/iPad build: Apple Vision Pro installs the visionOS build from the link, and every other device keeps getting the iPhone/iPad one. Each has its own versions; publish either again the same way to update it. A visionOS app's icon shows round, as on Apple Vision Pro. AppDrop reads the platform from the build's Info.plist, and checks it once the upload lands.
Mac builds
The same link can carry the app's Mac build too. Publish the zipped .app
(or a .app, zipped for you on macOS), .dmg or .pkg to the update link:
npx appdrop build/App.app --update https://appdrop.app/d/k3m9x2p7qa/manage#…
Macs then download it from the link, exactly as uploaded: AppDrop doesn't
re-sign Mac apps, so sign it with Developer ID and notarize it for it to open
without a warning. It must be the same app: the same bundle identifier, or Mac
Catalyst's maccatalyst. one. Every other device keeps getting the iPhone/iPad
build, and the Mac build has its own versions.
Reusing the currently published build number (CFBundleVersion, or Android’s
versionCode) fails before
uploading, with exit code 1. Increment the build number in the IPA, or explicitly
allow reuse with --force:
npx appdrop build/App.ipa --update <update-link> --force
Changing only the display version still requires --force. With --json, a
rejection prints { "code": "reused_build_number", "error": "…" } to stdout
and exits 1; stderr also explains how to proceed. A forced successful update
includes a warnings array in its JSON result. --force allows build-number reuse and the split-archive warning; the manage key and matching bundle identifier are still required.
npx appdrop links prints the share and update links of every app shared
from this machine. To let an agent take over an app, give it the update link. With --json, a
new link's manageUrl and manageKey are printed once, so a script can store
them as secrets; --key and $APPDROP_MANAGE_KEY work with a plain share
link, and keys are also saved in ~/.appdrop/drops.json ($APPDROP_HOME).
Your AppDrop account
npx appdrop login
links this computer to your AppDrop account. It prints a link; open it on any
device where you're signed in to appdrop.app, approve it, and the command
finishes. Every app already shared from this computer joins the account, and so
does each one shared or updated from then on. Apps in your account show on
appdrop.app/account, where you manage them signed in, without their update
links. npx appdrop logout unlinks the computer; the apps stay in the account.
Without a login, each upload suggests npx appdrop login, and --json results
say "account": "not-linked". They also include claimUrl, a link that adds
that one app to your account with one tap. Like the update link, it can
publish to the app, so keep it private. A linked computer's --json results
say "account": "added" instead.
The login is saved in ~/.appdrop/account.json ($APPDROP_HOME), readable
only by you. It can add and manage apps, nothing else, and lasts a year, or
until you remove the computer under Computers on appdrop.app/account.
For scripts and agents
Only the link goes to stdout (progress goes to stderr), so it can be captured:
url=$(npx appdrop build/App.ipa)
--json prints the details instead:
{"url":"https://appdrop.app/d/k3m9x2p7qa","manageUrl":"https://appdrop.app/d/k3m9x2p7qa/manage#…","id":"k3m9x2p7qa","revision":1,"name":"My App","bundleId":"com.example.app","version":"42","shortVersion":"1.2.0","size":31457280,"hasIcon":true,"manageKey":"…"}
The exit code is non-zero when the file isn't a supported app or the upload fails.
Android/Mac JSON results also include artifactType: apk, apks, or macos-zip. Android results also include androidSigning
with SHA-256 certificate fingerprints and whether the key is AppDrop-managed.
Options
| Option | |
|---|---|
--update <link> | Publish the app as a new version of an existing app. Takes the update link, or a share link plus --key. |
--key <key> | The key, when --update is given a share link (also $APPDROP_MANAGE_KEY). |
--json | Print the result as JSON. |
--force | Allow split .apks archives and reuse of the published build number; never signer changes. |
--origin <url> | Use another AppDrop server (also $APPDROP_ORIGIN). |
-h, --help | Show help. |
Uploads can be up to 500 MB. An .ipa can be signed or unsigned because it is
re-signed for each recipient. Already signed Android apps and Mac apps keep their existing signatures.
AppDrop reads the app’s name, identifier, version, and available PNG icon to show
on its page. The CLI has no dependencies.
Versions before 0.3 paired an iPhone from a Mac (npx appdrop <code>).
Pairing now happens on the iPhone through Tuft, so that command is gone.