Skip to content

gklsan/file_transfer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Gklsan Wifi Transfer

A minimal, cross-platform (Android / Windows / macOS) app for sending files directly between devices on the same WiFi network — no internet, no cloud server, no account.

  • Discovery: UDP broadcast on port 58211 (every ~3s), no mDNS/Bonjour dependency.
  • Transfer: a custom length-prefixed TCP protocol (no HTTP framework) — one control connection for the accept/reject handshake, then up to N parallel data connections (default 4, "quick mode") so multiple files move at once instead of one at a time.
  • Security model: every incoming transfer needs a manual Accept/Reject on the receiving device, like AirDrop. There's no transport encryption in v1 (plain TCP) — fine for trusted home/office WiFi; treat it like AirDrop, not like something for open/public networks.
  • Stack: Tauri 2 + Rust backend, plain HTML/CSS/vanilla JS frontend (no framework, no bundler — Tauri's built-in dev/build pipeline serves src/ directly).

See docs/ for the architecture, high-level, and low-level design documents — architecture.md, high-level-design.md, low-level-design.md.


1. Prerequisites

macOS (building for macOS, or doing any local dev)

  • Xcode Command Line Tools: xcode-select --install
  • Rust via rustup (not just the Homebrew rust formula — rustup is what manages the Android cross-targets below):
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

    If you also have Rust installed via Homebrew: check which rustc afterwards. If it prints /usr/local/bin/rustc or /opt/homebrew/bin/rustc instead of $HOME/.cargo/bin/rustc, Homebrew's (older) rustc is shadowing rustup's — see Troubleshooting for the permanent fix. This causes cargo build/Android build failures like rustc X.Y.Z is not supported by the following packages since some dependencies need a newer compiler than Homebrew's formula ships.

  • Node.js 18+ (any recent LTS). Via nvm:
    nvm install 18
    nvm use 18

Windows (building for Windows)

  • Rust via rustup (MSVC toolchain — the installer will prompt to install the Visual Studio Build Tools / C++ workload if missing).
  • Node.js 18+.
  • WebView2 runtime (preinstalled on current Windows 10/11).

macOS cannot cross-compile a Windows build — there's no supported Tauri path for that. Build on an actual Windows machine, or use the GitHub Actions workflow in this repo (see §4 CI).

Android (building/running the Android app)

  1. Rust Android targets:
    rustup target add aarch64-linux-android armv7-linux-androideabi \
      i686-linux-android x86_64-linux-android
  2. Android Studio (or just the SDK command-line tools), then via its SDK Manager (or sdkmanager on the CLI) install:
    • NDK (side by side) — this project was built and tested against 27.1.12297006. Tauri's docs deliberately don't pin a version; if a newer NDK is offered, it will very likely still work.
    • Android SDK Platform 34 and Build-Tools 34.0.0 (or newer).
    • Command-line Tools (latest) — needed for sdkmanager itself.
  3. Environment variables (add to your shell profile):
    export ANDROID_HOME="$HOME/Library/Android/sdk"          # macOS
    export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/27.1.12297006"
    export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"  # macOS
    export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
    On Linux, ANDROID_HOME is typically ~/Android/Sdk and JAVA_HOME points at Android Studio's bundled jbr under its install directory (or any JDK 17/21).
  4. npx tauri android init has already been run for this project (its output, src-tauri/gen/android/, is committed) — you don't need to redo it, just npm install and build (see below).

2. Running locally (dev mode)

npm install
npm run tauri dev

This opens a native window with hot-reload on frontend changes (Rust changes trigger a recompile + relaunch). Works the same on macOS, Windows, and Linux.

Testing discovery/transfer with two instances on one machine

Two instances need separate identities (device name/id) but don't need manually-offset ports — the discovery socket uses SO_REUSEPORT so multiple local instances share port 58211 fine, and the TCP control/data ports are always OS-assigned. Just build once and run the binary twice with a different profile env var:

cargo build --manifest-path src-tauri/Cargo.toml
./src-tauri/target/debug/gklsan_wifi_transfer &
GKLSAN_WIFI_TRANSFER_PROFILE=b ./src-tauri/target/debug/gklsan_wifi_transfer &

Each profile gets its own identity file (identity.json / identity-b.json under the OS app-data directory), so the two windows show up as distinct peers to each other within one beacon interval (≤ ~5s).


3. Building

One command

A single machine cannot cross-build other OSes' installers — Tauri only bundles for the host platform. So "build everything" comes in two flavors:

npm run dist       # every installer format THIS host OS can make, into dist/
npm run dist:ci    # macOS + Windows + Android via CI, downloaded into dist-ci/
  • npm run dist (scripts/build-local.sh) runs tauri build for the host and collects the result into dist/: .dmg/.app on macOS, .msi/.exe on Windows, .deb/.rpm/.AppImage on Linux. It also builds the signed Android APK when ANDROID_HOME is set, and skips Android otherwise.
  • npm run dist:ci (scripts/build-all-os.sh) is the only way to get every OS's installer from one command: it triggers the GitHub Actions matrix (each OS on its own runner), waits for it, and downloads all artifacts into dist-ci/. Needs the gh CLI authenticated and push access, and it builds the pushed ref — commit & push first, since CI never sees local uncommitted changes.

The per-platform sections below are the manual equivalents if you'd rather run one target at a time.

macOS

npm run tauri build -- --target universal-apple-darwin

Output: src-tauri/target/universal-apple-darwin/release/bundle/dmg/*.dmg and .../macos/*.app.

Windows (run on Windows)

npm run tauri build

Output: src-tauri\target\release\bundle\msi\*.msi and ...\nsis\*.exe.

Android

Don't build --apk --debug with no target filter — that produces a "universal" APK bundling all 4 CPU architectures with unstripped debug native libraries and no code shrinking, which comes out to ~475MB. Always restrict to your phone's architecture (almost every phone made since ~2017 is aarch64; check yours under Settings → About phone if unsure).

Recommended — small AND directly installable in one command:

npm run android:release             # aarch64 by default, ~6.9MB
npm run android:release -- armv7    # or: aarch64 | armv7 | i686 | x86_64

This runs scripts/android-release-signed.sh: builds a release APK (LTO + strip + R8 shrinking) for the target architecture, then zipaligns and signs it with your local ~/.android/debug.keystore — so the result is small and installable without any extra steps. Output: src-tauri/gen/android/app/build/outputs/apk/universal/release/gklsan_wifi_transfer-release-signed.apk. Install via adb install <path> or copy it to the phone and tap it (allow "install from unknown sources" the first time). Only files with -signed in the name are ever installable — app-universal-release-unsigned.apk (the plain Gradle output, also present alongside it) will fail with "app not installed as package appears to be invalid" if you tap it directly, since it has no signature at all.

Signing with the debug keystore is fine for sideloading to your own device, but not valid for Play Store distribution — that needs a real release keystore, not set up in this project yet.

Quick debug build (no signing step needed, Gradle auto-signs debug builds, but bigger since nothing is optimized/shrunk):

npx tauri android build --apk --debug -t aarch64   # ~129MB

Run it live for development:

npx tauri android dev

This deploys to a connected device or running emulator and streams logs.

Emulator networking limitation: the Android emulator's default networking on macOS/Linux is NAT'd into an isolated subnet — it will not see real LAN UDP broadcasts from other devices (including your host machine). Use the emulator only to confirm the APK installs/launches and the UI renders. For actual discovery/transfer testing, install on a physical Android phone on the same WiFi network as your other test device.


4. Running across real devices

  1. Put all devices on the same WiFi network (same subnet — most home networks are fine; corporate/guest networks sometimes isolate clients from each other, which will break discovery).
  2. Launch Gklsan Wifi Transfer on each device. They should appear in each other's "Nearby devices" list within a few seconds.
  3. Click Send files on one device, pick files, and the receiving device gets an Accept/Reject prompt — nothing transfers until it's accepted.
  4. Settings (gear icon) lets you rename the device, change the save folder (desktop only — Android always saves to the app's own storage, no SAF folder picker yet), and toggle Quick mode / max-parallel-connections (default 4) for how many files transfer simultaneously.

If a firewall is active (Windows Defender Firewall, macOS Application Firewall), you may be prompted to allow Gklsan Wifi Transfer to accept incoming network connections the first time it runs — allow it, otherwise peers won't be able to open the control/data TCP connections to you.


5. CI / Releases

.github/workflows/build.yml builds all three platforms on every push to main and on v* tags:

  • build-macos → universal .dmg / .app
  • build-windows.msi / .exe
  • build-android → debug-signed .apk

Pushing a tag like v0.1.0 additionally runs the release job, which attaches all three artifacts to a GitHub Release — the easiest way to get a Windows or Android build without owning that hardware:

git tag v0.1.0
git push origin v0.1.0

For manual/non-tag runs, artifacts are still uploaded per-job and downloadable from the Actions run page (Actions → the workflow run → Artifacts section) for 90 days by default. npm run dist:ci automates that whole loop — dispatch the workflow, wait, and pull every artifact into dist-ci/ locally (see §3 One command).


Troubleshooting

  • Android/cargo build fails with rustc X.Y.Z is not supported by the following packages (e.g. darling, plist, time requiring a newer rustc): you have Rust installed both via rustup and via Homebrew, and Homebrew's older rustc is winning the PATH race. This happens because ~/.zshenv sourcing . "$HOME/.cargo/env" early prepends ~/.cargo/bin, but Homebrew's brew shellenv (typically in ~/.zprofile, which runs after .zshenv for login shells) prepends /usr/local/bin or /opt/homebrew/bin again afterwards — and since ~/.zshrc sourcing cargo/env a second time is a no-op (it only prepends if ~/.cargo/bin isn't already in PATH), Homebrew ends up first. Confirm with which rustc — it should print something under $HOME/.cargo/bin, not /usr/local/bin or /opt/homebrew/bin. Permanent fix: add this as the very last line of ~/.zshrc:
    export PATH="$HOME/.cargo/bin:$PATH"
    Open a new terminal (or source ~/.zshrc) and re-check which rustc. This project's ~/.zshrc already has this fix applied as of the initial setup — if you're on a different machine or it regressed, this is the fix to reapply. Doesn't require uninstalling Homebrew's rust formula, just makes rustup's shim win.
  • npm install warns about plaintext HTTP to the npm registry: that's a pre-existing registry=http://... entry in your global ~/.npmrc, unrelated to this project. Consider switching it to https:// at some point.
  • Two local instances don't see each other: check nothing else on the machine is already bound to UDP 58211 without SO_REUSEPORT support (unlikely, but a stale process from a previous run can hold the port — lsof -i :58211 to check), and confirm both processes actually started (ps aux | grep gklsan_wifi_transfer).
  • Android build fails on NDK/SDK version mismatches: re-run sdkmanager --list_installed and make sure the ndk;<version> folder matches what ANDROID_NDK_HOME points at.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages