trueToastedCode/webdrop

★ 0Forks 0GoGitHub ↗Compare

README

LAN WebDrop

Ein lokales "WebDrop" für dein Netzwerk: jeder Rechner findet andere Rechner im selben Netzwerk automatisch und kann Dateien sowie Zwischenablage-Inhalte teilen.

Kein Sicherheits-/Verschlüsselungsmechanismus - bewusst, siehe Anforderungen. Nur für vertrauenswürdige, isolierte lokale Netzwerke gedacht.

Wichtig: native Ausführung vs. Docker

Es gibt zwei Betriebsmodi, je nach Zielplattform:

Linux (nativer Docker-Host): docker compose up --build -d mit network_mode: host funktioniert hier wie gedacht - der Container nutzt direkt die echte Netzwerkschnittstelle des Rechners, UDP-Broadcast erreicht das reale LAN.

Windows und Mac (Docker Desktop): Docker Desktop läuft intern in einer VM mit eigenem virtuellem Netzwerk (auch bei network_mode: host). UDP-Broadcast käme dort nie aus der VM heraus ins echte LAN, und selbst die Web-UI wäre vom eigentlichen Host aus nicht ohne Port-Mapping erreichbar. Auf Windows/Mac deshalb die native Binary direkt ausführen, kein Docker.

Die Binary ist eine einzelne, portable Datei (Frontend ist eingebettet) - kein Installer, keine Laufzeitumgebung nötig.

Binaries bauen

Ohne lokal installiertes Go (nutzt Docker als Build-Umgebung):

./scripts/build-all-in-docker.sh

Mit lokal installiertem Go:

./scripts/build-all.sh

Beides erzeugt in ./dist/:

  • webdrop-linux-amd64 / webdrop-linux-arm64
  • webdrop-windows-amd64.exe
  • webdrop-darwin-amd64 (Mac Intel) / webdrop-darwin-arm64 (Mac Apple Silicon)

Cross-Compiling funktioniert ohne Fallstricke, weil der komplette Code reines Go mit Standardbibliothek ist (CGO_ENABLED=0) - keine plattformspezifischen Abhängigkeiten.

Starten

Native Binary (Windows/Mac, empfohlen):

./webdrop-darwin-arm64        # bzw. die passende Datei für die Plattform

Danach http://localhost:8080 im Browser öffnen. Persistente Daten (Node-ID, Trust-Liste) landen automatisch in einem plattformüblichen Verzeichnis (%AppData%\webdrop unter Windows, ~/Library/Application Support/webdrop unter Mac, ~/.config/webdrop unter Linux).

Erste Startschwierigkeiten, die keine Bugs sind, sondern normales Betriebssystem-Verhalten:

  • Windows Firewall fragt beim ersten Start, ob die App im Netzwerk kommunizieren darf - "Zulassen" bestätigen (private Netzwerke reichen).
  • macOS Gatekeeper blockiert unsignierte Binaries beim Doppelklick - einmalig per Rechtsklick → Öffnen bestätigen.

Docker (nur für native Linux-Hosts):

docker compose up --build -d

Konfiguration (Umgebungsvariablen)

Variable Standard Bedeutung
HTTP_PORT 8080 Port der Web-UI / API
DISCOVERY_PORT 41234 UDP-Port für Broadcast-Discovery
BROADCAST_ADDR leer (= automatische Multi-Interface-Erkennung) fester Override, z.B. 255.255.255.255:41234, falls Auto-Erkennung nicht passt
ADVERTISE_ADDR leer (= pro Peer automatisch ermittelt) fester Override, falls Auto-Erkennung nicht passt (z.B. Port-Weiterleitung/NAT)
PEER_TIMEOUT 6s Peer gilt nach dieser Zeit ohne Lebenszeichen als offline
TRANSFER_TTL 30m wie lange ein Datei-Angebot gültig bleibt
DATA_DIR plattformabhängig (s.o.) persistente Daten (ID, Trust-Liste, Uploads)
WEB_DIR leer (= eingebettetes Frontend) Override für lokale Frontend-Entwicklung ohne Neu-Kompilieren

Architektur im Überblick

cmd/webdrop/main.go     Verdrahtet alles, liest Konfiguration aus ENV
internal/id             Persistente Node-Identität (UUID + Name)
internal/peers          Peer-Registry mit Timeout-/Prune-Logik
internal/discovery      UDP-Broadcast Announce/Listen
internal/trust          Persistente Trust-Liste pro Peer
internal/transfer       Download-Token-Verwaltung für Dateien
internal/api            HTTP-API + Server-Sent-Events (SSE) für Live-Updates
internal/webui          Eingebettetes Frontend (go:embed)
scripts/                Cross-Compile-Skripte für alle Zielplattformen

Jedes Package ist unabhängig testbar und über klar definierte Schnittstellen verbunden - gedacht, um später einzelne Teile auszutauschen oder zu erweitern (z.B. Verschlüsselung in internal/transfer, eine andere Discovery-Methode in internal/discovery, Auth-Handling in internal/api), ohne den Rest anfassen zu müssen.

Warum diese technischen Entscheidungen?

  • UDP-Broadcast statt mDNS/Bonjour: volle Kontrolle über Timing. Das Broadcast-Intervall (Standard 1,5s) und der Peer-Timeout (Standard 6s) sorgen dafür, dass ein Peer nach einem Netzwerk-Aussetzer innerhalb weniger Sekunden verschwindet bzw. wieder auftaucht - nicht nach Stunden.
  • Broadcast an spezifische Subnetz-Adressen statt an die globale 255.255.255.255: Senden an die globale Broadcast-Adresse überlässt dem Betriebssystem, über welches Interface das Paket geht. Sobald mehrere Netzwerk-Interfaces gleichzeitig aktiv sind (Docker Desktop, VirtualBox, Hyper-V, WSL und VPN-Clients legen alle automatisch eigene virtuelle Adapter an), kann diese Wahl auf ein isoliertes virtuelles Netz fallen - das Senden meldet trotzdem Erfolg, das Paket erreicht aber nie andere Rechner im echten LAN. WebDrop ermittelt deshalb bei jedem Announce alle aktiven, "broadcast-fähigen" Interfaces (internal/discovery/interfaces.go) und sendet gezielt an die Subnetz-Broadcast-Adresse jedes einzelnen (z.B. 192.168.0.255 statt 255.255.255.255) - jedes Subnetz hat dadurch eine eindeutige Route. Über BROADCAST_ADDR lässt sich das bei Bedarf weiterhin auf eine feste Adresse überschreiben.
  • Frontend per go:embed: eine einzelne portable Binary statt Binary + Ordner + Pfad-Konfiguration - wichtig für native Ausführung auf Windows/Mac ohne Docker.
  • Adress-Ermittlung pro Ziel-Peer statt einer global geratenen Adresse: Beim Datei-Versand wird die eigene, für den jeweiligen Empfänger korrekte lokale Adresse erst im Moment des Kontakts ermittelt (internal/netutil.LocalAddrFor), nicht einmalig beim Start global geraten. Eine global geratene Adresse kann auf Rechnern mit mehreren aktiven Interfaces (Docker/VirtualBox/Hyper-V/WSL/VPN) für einen bestimmten Peer schlicht falsch sein - anders als beim Broadcast (siehe oben) ist das Routing zu einem konkreten, bekannten Ziel-Host aber nicht mehrdeutig, der Kernel kennt die richtige Antwort in diesem Moment immer.
  • Downloads per normalem HTTP-GET: kein eigenes Übertragungsprotokoll - der sendende Knoten bietet die Datei unter einer Download-URL an, der Zielknoten lädt sie ganz gewöhnlich herunter (inkl. Keep-Alive, Range-Requests durch http.ServeContent).
  • Server-Sent Events statt WebSocket: wir brauchen nur eine Push-Richtung (Server → Browser für Peer-Updates und eingehende Transfers). SSE braucht dafür keine externe Abhängigkeit und reconnected im Browser automatisch von selbst.
  • Clipboard-Autoschreiben: Browser erlauben navigator.clipboard.write nur zuverlässig ohne Nutzergeste, wenn der Tab fokussiert ist. Bei vertrauten Peers UND fokussiertem Tab wird automatisch geschrieben, sonst erscheint ein Hinweis mit "Übernehmen"-Button.

Tests

go test ./...

~36 Unit-/Integrationstests decken die Kernlogik ab: Peer-Timeout-Verhalten, ID-/Trust-Persistenz über Neustarts, Discovery-Paket-Handling (inkl. Ende-zu-Ende-Test zwischen zwei simulierten Knoten), Transfer-Token-Handling, das eingebettete Frontend, und alle HTTP-Handler (inkl. Trust-gesteuertes Auto-Accept-Verhalten, mit echtem Mock-Peer-Server statt echtem Netzwerk).

Bekannte Grenzen / mögliche Erweiterungen

  • Keine Verschlüsselung, keine Authentifizierung (bewusst, siehe oben)
  • Automatisches Clipboard-Schreiben funktioniert nur bei fokussiertem Browser-Tab (Browser-Beschränkung, keine App-Entscheidung)
  • Docker Desktop (Windows/Mac) eignet sich nicht als Laufzeitumgebung für dieses Projekt - dort die native Binary nutzen (siehe oben)
  • Für spätere Erweiterung vorgesehen: Protokollversion im Discovery-Announcement (discovery.ProtocolVersion), damit sich das Format später abwärtskompatibel weiterentwickeln lässt

Contributors

trueToastedCode

Issues