Repository navigation
Releases: gclinian/SafeDrop
Release list
SafeDrop v1.7.1
Community contribution — interface-aware desktop discovery. Thanks to
@JianBin0218 (PR #1).
Fixed
- Desktop discovery now computes each interface's directed broadcast
from its real netmask instead of assuming /24. The previous /24
guess produced the wrong broadcast on non-/24 LANs — most notably an
iPhone Personal Hotspot (172.20.10.xis a /28, broadcast
172.20.10.15), so phones on a hotspot never discovered the desktop.
Now usespsutilto enumerate active, up interfaces and derive the
broadcast per interface (safedrop/discovery.py). - Multi-homed machines send each directed broadcast out the right
interface._broadcast_endpoints()pairs each broadcast target
with its source IP and binds the send socket to that source, so a
box with e.g. a Radmin/VPN adapter + Wi-Fi no longer leaks the
Wi-Fi broadcast onto the VPN interface (or vice-versa). - The status bar's local-IP display now lists every active interface
IP (_local_ip_display()).
Changed
- New runtime dependency:
psutil>=5.9(interface enumeration). pyproject.toml→1.7.1.
Tests
tests/test_discovery.pyrewritten around the new
BroadcastInterfacemodel: netmask-derived broadcast (incl. the /28
hotspot case), down-adapter skipping, per-source-IP endpoint binding,
multi-interface IP display. 104 / 104 green.
Note: this release is desktop-Python only. The iOS native discovery
already computes per-interface broadcasts from netmask via
getifaddrs(shipped in 1.7.0), so it was already correct for the
hotspot case.
SafeDrop v1.7.0
The "iOS actually works on real hardware" release. Fixes the iOS
discovery interop bug (iOS couldn't see or be seen by other devices)
and completes sender-side pair-code display on iOS + Android.
Fixed — iOS discovery interop (the big one)
-
iOS now broadcasts to the subnet-directed address + loopback, not
just255.255.255.255. The global broadcast is routinely dropped on
real Wi-Fi (VPN default routes, AP client isolation, iOS's network
stack) — the subnet broadcast (e.g.192.168.1.255, computed per
interface from its netmask viagetifaddrs) is what actually reaches
peers. This is why other devices couldn't see iOS. (Same root
cause + fix as the Python discovery fix in 1.6.x.) -
iOS
Discoveryrewritten from anactorto afinal classon a
dedicatedDispatchQueue. The listen loop's blockingrecvfrom
(1 s timeout) was running on Swift's cooperative thread pool (via
actor+Task.detached), which starves the pool and stalls the
loops on real devices — the exact anti-patternTransferManager
already fixed. This is why iOS couldn't see others. Now all
blocking socket I/O runs onioQueue; peer state is lock-guarded. -
Verified on the iOS Simulator: a Python peer discovers the iOS
device (via subnet broadcast192.168.0.100), and the iOS app's
Nearby devices list shows the Python peer — both directions.On a real device, iOS will show the Local Network permission
prompt on first launch (driven byNSLocalNetworkUsageDescriptionNSBonjourServices, already in Info.plist). The user must tap
Allow or discovery stays dark — this is an OS requirement, not
a SafeDrop setting.
Fixed — sender-side pair-code display (completes 1.6.2)
- iOS: a confirmation sheet now appears on the sender showing the
4-digit pair code while waiting for the receiver to Accept
(OutboundPairPromptpublished fromTransferManager.sendText/
sendFile, rendered asOutboundPairSheet). Auto-dismisses when the
transfer proceeds, completes, is rejected, or fails. - Android: same —
OutboundPairDialogderived from the outbound
transfer'spendingstate + non-emptypairCode, with a Hide
button. Both now honor SPEC §5.1 (both sides display the same code).
Changed
SPEC.md§5.1 documents the multi-target broadcast (loopback +
subnet + global) and the "blocking I/O off the cooperative pool"
rule for all platforms.pyproject.toml→1.7.0.
Tests
- Python suite unchanged at 103/103 (this release is iOS/Android +
SPEC; no Python code touched). iOS + Android both build clean.
SafeDrop v1.6.2
Bugfix — the desktop GUI sender never displayed the pair code,
breaking the visual man-in-the-middle check the protocol is designed
around. SPEC.md requires both sides to show the same 4-digit code;
the receiver's Accept dialog showed it, but the sender showed nothing,
so the user had nothing to compare against.
Fixed
- Sender-side pair-code verification (
safedrop/gui.py). When you
start a transfer, a "Verify pair code" window now appears on the
sender showing the 4-digit code, and stays up while waiting for the
receiver to Accept — mirroring the receiver's dialog. It auto-
dismisses once the transfer proceeds (transferring), completes, is
rejected, or fails. The code is already carried on the wire and in
TransferState.pair_code; this is purely the missing display.
Tests
- New
test_sender_surfaces_pair_code_before_transferin
tests/test_e2e.py: asserts the sender emits a non-empty 4-digit
pair code while the transfer is stillpending(before bytes flow),
and that it matches the code the receiver independently derives.
103 tests green.
Known gaps (follow-ups, not in this release)
- iOS surfaces the pair code only after the transfer finishes
(in a status line), not during the pre-Accept verification window. - Android sender does not show the pair code at verification time.
Both should mirror the desktop fix to fully honor SPEC §pair_code.
SafeDrop v1.6.1
Patch — make the desktop install path actually one line. No
protocol changes, no new dependencies.
Added
scripts/install.sh— one-key installer for macOS + Linux.
Verifies Python 3.10+, asserts tkinter is importable (with a clear,
platform-specific fix on failure), creates a hermetic venv at
~/.local/share/safedrop/venv, installssafedrop[mcp]from PyPI
(or--from-releaseagainst the latest GitHub Release wheel), and
dropssafedrop-gui/safedrop/safedrop-mcp/
safedrop-mcp-tokens/safedrop-agent/safedrop-beacon
launchers into~/.local/bin. Idempotent.scripts/install.ps1— PowerShell counterpart for Windows. Venv
lives at%LOCALAPPDATA%\SafeDrop\venv;.cmdlaunchers under
%LOCALAPPDATA%\SafeDrop\bin. Same flow, same error messages.
Changed
- README "Quick start" now leads with the one-line install:
(and the
curl -fsSL https://raw.githubusercontent.com/gclinian/SafeDrop/main/scripts/install.sh | bashiwr | iexWindows equivalent). The previous
git clone+ venv +pip install -eflow is preserved under a new
Desktop — manual install subsection for the security-conscious. docs/landing.htmlinstall snippet updated to match.
Tests
- Smoke-verified
scripts/install.sh --from-releaseagainst the
publishedv1.6.0wheel: pulls the .whl from GitHub Releases,
installs into a fresh venv, launchers fire correctly,
safedrop lsruns without errors.
SafeDrop v1.6.0
The "actually finish v1.6 + introduce the project" release. Closes
out the cross-platform native rendering of v1.6 primitives and ships
the docs needed to onboard new users / contributors.
Added — native notification rendering on all 3 platforms
- Python tkinter banner for inbound
show_notification.gui.py
subscribes tosafedrop.notifications.bus.on_notificationand pops
a transient Toplevel (auto-dismiss after 8 s, color-coded by
info/warn/error). - iOS UNUserNotificationCenter — new
show_notificationhandler
inios/SafeDrop/Tools.swiftdrops a system banner via
UNTimeIntervalNotificationTrigger. App requests.alert+.sound
authorization on launch. - Android NotificationManager — new
show_notificationhandler
inandroid/.../net/ToolRegistry.ktposts via
NotificationCompat.Builderwith a lazy-created channel. Added
POST_NOTIFICATIONSpermission to AndroidManifest and runtime
request viaActivityResultContracts.RequestPermissionin
MainActivity for API 33+. - Moved
notification_tools.pyfromsafedrop_mcp/to
safedrop/notifications.pyso the desktop GUI can use it without
the[mcp]extra installed. MCP server imports updated.
Added — iOS token management UI
- New
ios/SafeDrop/TokenAdminView.swift— SwiftUI screen that calls
the cross-device peer tools (tokens_list,tokens_mint,
tokens_revoke) on a selected SafeDrop peer (the desktop running
safedrop-mcp). Mint form with label / scope / TTL fields;
one-time secret reveal sheet on success. - Toolbar key icon in
HomeViewopensTokenAdminViewwhen the
selected peer advertisessafedrop.tools.
Added — introducing the project
docs/landing.html— single-page self-contained landing page
(no external deps, dark theme, hero + features + architecture
diagram + install snippet + cross-platform capability matrix +
security model + roadmap). The thing you link from a README badge.docs/pitch.md— 30-second + 2-minute pitches for a HN post /
Twitter thread.docs/demo-recipes.md— six tight live-demo recipes (each
~30-90 s), with exact commands + audience-facing punchline. Covers
cross-platform AirDrop, agent-with-phone-camera, two-agent mesh,
state handoff, mobile token mint, and Tailscale cross-LAN.
Changed
pyproject.toml→1.6.0.
SafeDrop v1.3.0 — the MCP fabric release
The "MCP fabric" release. SafeDrop is no longer just a LAN
file/clipboard tool — it's a scoped, auditable, cross-device Model
Context Protocol fabric that any AI agent (Claude Code, Cursor, Goose,
Cline, Hermes via Ollama, cloud agents over HTTPS, …) can plug into.
Downloads
| File | What it is | How to install |
|---|---|---|
SafeDrop-ios-1.3.0-unsigned.ipa |
iOS 17+ app, arm64, unsigned (208 KB) | Re-sign with your own Apple ID via AltStore or Sideloadly; SafeDrop uses no entitlements that break re-signing. Full guide: ios/DISTRIBUTION.md |
SafeDrop-android-1.3.0-debug.apk |
Android 8.0+ (API 26+) debug APK (19 MB) | adb install -r SafeDrop-android-1.3.0-debug.apk or sideload via file manager |
safedrop-1.3.0-py3-none-any.whl / .tar.gz |
Python desktop / CLI / MCP server | pip install safedrop-1.3.0-py3-none-any.whl[mcp] then safedrop-mcp or python -m safedrop |
Highlights
- iOS Phase 1 client — native Swift/SwiftUI, same X25519 + Fernet
protocol as Python and Android, byte-for-byte interoperable
(system_info,read_clipboard,write_clipboard). - MCP HTTP / Streamable transport with bearer-token auth + per-token
scope enforcement (safedrop-mcp --http). - Capability tokens (
safedrop-mcp-tokens mint --scope ... --ttl ...)
persisted to~/.safedrop/tokens.json, instantly revocable. - Per-agent policy via
--allow/--deny/--profile. Run
multiplesafedrop-mcpinstances side-by-side, each with its own
scope. - MCP bridge — import any other MCP server (
mcp-server-filesystem,
mcp-server-github, …) into the SafeDrop fabric; their tools become
callable from every paired peer on the LAN. register_local_tool— agents add new tools at runtime via
HTTP callback URLs.- Trust + audit UIs on Python, Android, and iOS — review and
revoke saved (peer, tool) decisions.
See CHANGELOG.md
for the full per-version log and MCP_AGENT_GUIDE.md
for the agent-integration deep dive.
Tests
Ran 38 tests in 14.0s — OK
Covers protocol e2e, cross-language interop (Python ↔ Kotlin ↔ Swift
all derive the same pair code from the same shared secret), policy,
tokens, HTTP transport, bridge, dynamic tool registration.
Platforms
| Platform | Status |
|---|---|
| macOS / Linux / Windows desktop | ✅ files, clipboard, MCP server, CLI, trust + audit UI |
| Android (8.0+) | ✅ files, clipboard, tools (take_photo etc.), trust + audit UI |
| iOS (17+) | ✅ Phase 1 — tools, trust + audit UI. File picker on the roadmap. |
License: MIT.