Commit Graph
13 Commits
Author SHA1 Message Date
TZGyn decaaa2de1 Use the tzgyn relay as the default backend
The CLI and iOS app now point at share.tzgyn.dev:9009 instead of the upstream croc relay. Both were verified against the deployed relay; local relays remain available through --relay and the app's Relay setting.
2026-10-26 22:32:15 +08:00
TZGyn 9a37e93cfc Add a native iOS client for relay-based file sharing
The SwiftUI app bundles the Go swarm engine through gomobile, so an iPhone is a real peer using the existing PAKE handshake, chunk verification, and relay fallback. XcodeGen builds the project and devicectl installs it on the paired phone.
2026-10-26 22:24:47 +08:00
TZGyn 29fa4a4c44 Stop tracking the compiled binary in version control
The ignore rule matched "share-*" but not the binary's actual name,
"share", so every build was committed and pushed. Ignore the binary
explicitly and untrack it; releases now carry the built artifacts.
v0.1.0
2026-09-11 16:38:59 +08:00
TZGyn 0110f9da69 Add a local web dashboard and "share web"
Daemon now also runs a plain HTTP server bound to 127.0.0.1 only (never
the network), serving an embedded vanilla HTML/CSS/JS page plus a small
JSON API (status/join/stop/leave/restart) that calls the same internal
methods the Unix-socket protocol already uses. No frontend framework or
build step — the page is embedded straight into the binary via
go:embed, keeping distribution a single file.

"share web" ensures the daemon is running, asks it (a new "web" control
action) for the dashboard's address, and opens it in the default browser
(open/xdg-open), falling back to printing the URL if that fails.

RoomStatus/FileStatus gained JSON tags for clean camelCase output, and
RoomStatus now carries RelayAddress so the dashboard can show it.
2026-07-20 16:26:34 +08:00
TZGyn 04613e6685 Add per-room stop/leave/restart, with a unified status display
- "share stop <code>" pauses just that room (kept in rooms.json and in
  "share status", so its code can still be looked up and resumed) without
  touching any other room or the daemon.
- "share leave <code>" forgets a room entirely — no bare "stop" collapses
  into "leave" anymore, since those need to be distinct: pausing must
  survive to be resumable, forgetting must not.
- "share restart <code>" now works whether the room is still active or
  already paused (falls back to the persisted list), rejoining with its
  original settings.
- "share status" shows the same layout for running and stopped rooms —
  an explicit running/stopped label, relay address, and file details in
  both cases. A stopped room's file details come from stat()ing its
  seed files (always locally complete); a stopped receiving room has no
  file list since that's only knowable once it reconnects.
2026-07-20 15:21:40 +08:00
TZGyn 84f3d62d68 Persist rooms across daemon restarts; add "share restart"; fix peer
count and a startup hang

- src/daemon/persist.go: rooms.json records every active room so a
  restarted daemon (crash, reboot, or "share stop" then relaunch) resumes
  them automatically. A --one-time room clears itself once it finishes
  on its own; anything else stays persisted since it only ever stops via
  an explicit Stop.
- "share restart": stops the running daemon, waits for it to actually
  release its socket, then relaunches it the same detached way
  EnsureRunning does.
- Fixed a real startup hang: resuming persisted rooms used to block
  before the daemon's Accept loop ever started, so a slow/unreachable
  relay in one persisted room could leave the daemon looking "alive" to
  IsRunning() while unable to answer any request — including its own
  successor's attempt to detect and clean up a stuck instance. Resuming
  now runs in the background instead. Added deadlines on both ends so a
  stuck daemon fails fast with an actionable message rather than hanging.
- Fixed peer counts being inflated by duplicate connections: two peers
  can end up with more than one link to each other (both sides happened
  to dial at once). Links now carry the remote peer's ID (known
  immediately when we dialed them, learned from their first manifest
  otherwise), and Status dedupes by that ID.
2026-07-20 14:29:42 +08:00
TZGyn c8f670e920 Add Dockerfile to deploy the relay server
Multi-stage build: compiles with CGO disabled in golang:1.25-alpine,
runs the static binary in a plain alpine:3.20 image. Defaults to
"share relay" on port 9009; override with extra args at `docker run`.
2026-07-20 12:29:46 +08:00
TZGyn 15a6658d1c Add "share status" command and a relay-piped fallback for cross-network peers
status: swarm.Client.Status()/RoomStatus/FileStatus report per-room peer
count and per-file chunk progress; daemon gets a "status" control action;
CLI gets "share status" to display it.

Relay fallback: peers advertising a LAN-only address can't be dialed
directly by peers on a different network. connectToPeer now races a
direct dial against a relay-piped connection (both sides derive the same
pairing key from their two IDs, independent of who arrives first) and
uses whichever succeeds. The relay grows a second, on-demand path
alongside room membership: a "pipe" request pairs two connections on a
key and forwards raw bytes between them from then on, same as the
original relay's behavior before the rendezvous-only redesign, just now
triggered per-pair only when direct connection fails.
2026-07-20 12:27:27 +08:00
TZGyn 26cef8ee12 Turn the client into a background daemon; add per-chunk hash verification
Daemon (src/daemon): a Unix-socket control server holds every room this
machine is participating in. "share send"/"share <code>" resolve paths to
absolute, ask the daemon to join (auto-starting it if needed), and exit
immediately instead of blocking. "share stop" leaves every room and shuts
the daemon down; a stale socket from a crashed daemon self-heals on next
start.

swarm.Client is now a Start/Wait/Stop handle instead of a single blocking
Run, so the daemon can hold many rooms concurrently.

Also added per-chunk sha256 verification: the original seeder publishes a
hash for every chunk alongside the whole-file hash, and each chunk is
checked against it the moment it arrives — before it's written to disk or
reshared to other peers — rather than only catching corruption after the
whole file is assembled.
2026-07-20 11:45:33 +08:00
TZGyn 34ce1f4e65 Remove all schollz/* dependencies: fork pake, replicate logger and CLI
- src/pake: trimmed fork of schollz/pake/v3, P256-only (stdlib
  crypto/elliptic), dropping the tscholl2/siec and filippo.io/edwards25519
  transitive deps along with it.
- src/logger: minimal in-repo replacement for schollz/logger with the
  same leveled-logging call surface.
- src/cli: replaced schollz/cli (urfave/cli fork) with a small
  flag-package-based parser for the send/relay/receive commands actually
  in use; subcommand keyword can appear before or after its flags.
- Dropped schollz/progressbar entirely (its show-progress code paths were
  already unreachable dead code since no CLI flag wired them up).

go.mod/go.sum no longer reference any schollz/* module.
2026-07-20 11:26:59 +08:00
TZGyn 3a0ab392c0 Add .gitignore for local-only reference and test scratch folders 2026-07-20 11:13:49 +08:00
TZGyn 7936de736d Turn the relay into a rendezvous-only swarm: everyone sends and receives
Peers join a room and the relay just broadcasts membership (id + dialable
address); peers then connect to each other directly, PAKE-handshake per
link using the room codephrase, and exchange 1MB chunks. Resume reuses
croc's zero-block scan to detect already-received chunks, whole-file hash
verifies completion, and every peer keeps reseeding what it has to others
until stopped (--one-time restores exit-on-complete for a quick fetch).
2026-07-20 11:12:18 +08:00
TZGyn 95726d9e45 Initial commit: minimal relay server and CLI structure
Relay pairs connections by room name and pipes bytes between them,
with no password/PAKE handshake at the relay layer — codephrase-based
encryption is left to the client side of the transfer.
2026-07-20 10:13:37 +08:00