Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

Skiff

Direct LAN file & content sharing between macOS and Android — like AirDrop, but cross-platform.

  • Discovery: mDNS / Bonjour (_skiff._tcp)
  • Transport: TLS 1.3 with mutual TLS authentication
  • Pairing: 6-digit PIN with HMAC verifier + cert fingerprint pinning
  • Payloads: files (streamed, no size cap), images, video, screenshots, URLs, text, drag-and-drop, share-sheet target

Repo layout

.
├── PROTOCOL.md             Wire spec
├── assets/icon.svg         Source artwork
├── macos/Drop              SwiftUI app (Swift 6, macOS 14+)
│   └── tools/build-app.sh  Build Skiff.app for distribution
└── android/drop            Jetpack Compose app (Kotlin 2.1, min SDK 29)

Building & running

macOS

Need Xcode 16+ (provides the Swift 6 toolchain).

cd macos/Drop
swift run Skiff                              # development run
# or build a distributable bundle
bash tools/build-app.sh                     # produces macos/Drop/build/Skiff.app
# Icon is bundled automatically (from macos/Drop/Resources/Skiff.icns).
# To refresh after editing assets/icon.svg:
bash tools/generate-icon.sh
open build/Skiff.app

On first launch macOS asks for Local Network access — allow it.

Distributing on a website

cd macos/Drop
bash tools/build-app.sh
bash create_dmg.sh          # optional — produces build/Skiff.dmg

Host Skiff.dmg (or zip Skiff.app) on your site. macOS Gatekeeper will warn on first launch because the app is not notarized — users right-click → Open once, then it runs normally. That's expected for unsigned distribution.

Optional: Developer ID + notarization (no Gatekeeper warning)

Requires a $99/year Apple Developer account. See tools/release-app.sh or:

bash tools/build-app.sh
codesign --force --options runtime --timestamp \
    --sign "Developer ID Application: YOUR NAME (TEAMID)" \
    build/Skiff.app
ditto -c -k --sequesterRsrc --keepParent build/Skiff.app build/Skiff.zip
xcrun notarytool submit build/Skiff.zip --apple-id you@example.com \
    --team-id TEAMID --password "@keychain:AC_PASSWORD" --wait
xcrun stapler staple build/Skiff.app
hdiutil create -volname Skiff -srcfolder build/Skiff.app -ov \
    -format UDZO build/Skiff.dmg

Android

cd android/drop
gradle wrapper --gradle-version 8.10.2     # one-time
./gradlew :app:installDebug                # USB or emulator

Targets Android 10+ (API 29).

For a signed release build to distribute via your site or Play Store:

cp keystore.properties.example keystore.properties   # edit with your keystore
./gradlew :app:assembleRelease
# Output: app/build/outputs/apk/release/app-release.apk (signed when keystore.properties exists)

Security model

  • Mutual TLS with both certs self-signed and pinned by SHA-256 of the cert DER. A MITM presenting a different cert fails the handshake.
  • Pairing PIN uses HMAC-SHA256 with the PIN as the key and the two cert fingerprints as the message. The receiver rate-limits to 3 failures before regenerating the PIN.
  • Trust binding persists across restarts: each side records the other's (id, fingerprint) in trust_store.json. Any future TLS connection presenting a mismatched fingerprint is rejected.

Honest limitations

Item State Plan
Private keys in Keychain (Mac) / AndroidKeyStore (new installs) ✅ Legacy file keys migrate on load
HMAC-PIN handshake is not a true PAKE Offline brute-forcing the PIN is possible if the wire is captured, but rate-limiting + ephemeral PIN narrows the window SPAKE2 (RFC 9382) — bigger refactor
Mac → Android mDNS sometimes blocked by corporate VPNs Worked around by typing IP:port directly via the + icon on Android QR-code pairing
Foreground service can be killed by aggressive OEM battery-savers First-launch dialog points at the battery whitelist n/a

Build status

Milestone Status
1. Protocol spec ✅
2. Bonjour / NSD discovery ✅
3. PIN pairing ✅
4. Cert fingerprint pinning ✅
5. Text / URL / file transfer ✅
6. Mutual TLS ✅
7. Streaming file transfer (no cap) ✅
8. Foreground service (Android) ✅
9. Drag & drop (Mac) + share sheet (Android) ✅
10. Manual IP fallback (Mac + Android) ✅
11. Inbox polish (image previews, swipe-to-delete, smart timestamps) ✅
12. .app bundle script ✅
13. Crash logging ✅

Verifying it works

# from the Mac terminal, both devices should appear:
dns-sd -B _skiff._tcp local.

# probe the local server:
curl -k --cert /tmp/yourcert.pem --key /tmp/yourkey.pem \
    https://<mac-ip>:<port>/v1/info

Inside the app:

  1. Run Skiff on both devices.
  2. On one device tap the lock / "Show PIN" button — a 6-digit PIN appears.
  3. On the other device tap that peer → enter the PIN → Pair.
  4. Once a green shield appears, tap the peer again to send a message, file, or use drag-and-drop (Mac) / share sheet (Android).

If devices don't see each other, tap + on either platform and type the other device's IP:port from its footer (e.g. 192.168.0.9:54321).

What still needs you (not code)

  • Android signing keystore for release APKs. keytool -genkey -v ....
  • Privacy policy to host on your site — draft in docs/privacy-policy.md (add your contact email).
  • Crash reporting backend (Sentry / Bugsnag / Crashlytics) if you want remote reports. Skiff already writes local crash logs to disk; CrashLogger.install() is the hook.
  • Beta with real users. Plan for at least 5–10 testers across different Wi-Fi networks; OEM Android skins kill foreground services in unpredictable ways.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages