feat: download dirs, file transfer resume, Wails desktop app, PWA, CI
Daemon:
- Per-network download directories (-download-dir flag, set_download_dir IPC)
- File transfer resume after disconnection: .tmp.meta sidecars survive
interruption; resume_offset in file-accept lets sender seek and continue
- set_download_dir IPC command; download_dir reported in state_snapshot
Protocol:
- PeerMessage.ResumeOffset (EXT-006) for file transfer resume
- IpcMessage.ShareNetworks json tag changed from "networks" to "network_ids"
to fix duplicate json tag collision with Networks []NetworkInfo
Desktop app (cmd/app):
- Wails v2 shell embedding daemon logic directly (no subprocess)
- System tray on Linux/Windows via getlantern/systray; macOS hides to Dock
- OS notifications for message_received and file_complete via Wails events
- notray build tag for headless/CI builds without GTK tray headers
- build-app.sh: builds web frontend, copies dist, runs wails build
Web / PWA:
- manifest.json + Apple touch icon meta tags for mobile "Add to Home Screen"
- PNG icons (192px, 512px, 180px) generated from SVG
- Wails EventsOn("notify") hook in App.tsx for native OS notifications
CI:
- .gitea/workflows/build.yml: server binaries cross-compiled for 5 platforms,
desktop app for Linux amd64, release artifacts published on v* tags
Docs:
- README: download dir, file transfer resume, desktop app, PWA, CI sections
- EXTENSIONS.md: EXT-004 daemon mode marked shipped; EXT-006 resume added
- FUTURE.md: roadmap updated
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -117,8 +117,8 @@ proper signed invite (if the network enforces it) to be accepted by peers.
|
||||
### New IPC commands
|
||||
|
||||
```jsonc
|
||||
{"type":"add_share","path":"/home/alice/Music"} // global
|
||||
{"type":"add_share","path":"/home/alice/Docs","networks":["abc123"]} // scoped
|
||||
{"type":"add_share","path":"/home/alice/Music"} // global
|
||||
{"type":"add_share","path":"/home/alice/Docs","network_ids":["abc123"]} // scoped
|
||||
{"type":"remove_share","path":"/home/alice/Music"}
|
||||
{"type":"list_shares"}
|
||||
```
|
||||
@@ -148,7 +148,7 @@ entries from all applicable share roots, with relative `path` fields
|
||||
|
||||
## EXT-004 — TURN Relay (browser mode)
|
||||
|
||||
**Status:** implemented (browser mode); pending (daemon mode)
|
||||
**Status:** implemented (browser mode + daemon mode)
|
||||
**Affects:** ICE server configuration only, no wire changes
|
||||
|
||||
The browser adapter reads `WASTE_CONFIG.turnURL` and `WASTE_CONFIG.turnSecret`
|
||||
@@ -171,3 +171,56 @@ this field continue to use `name` for display and download requests.
|
||||
|
||||
`MsgFileListReq` / `get` requests use `path` as the lookup key when present,
|
||||
falling back to `name` for backward compat with peers that don't send `path`.
|
||||
|
||||
---
|
||||
|
||||
## EXT-006 — File Transfer Resume
|
||||
|
||||
**Status:** implemented (daemon mode)
|
||||
**Affects:** `file-accept` wire message (additive field)
|
||||
|
||||
### Motivation
|
||||
|
||||
A transfer interrupted mid-stream (peer disconnect, network drop) can be
|
||||
continued from where it left off on the next offer of the same file, avoiding
|
||||
a full re-download.
|
||||
|
||||
### Protocol change
|
||||
|
||||
`file-accept` gains one optional field:
|
||||
|
||||
```json
|
||||
{ "type": "file-accept", "xid": "...", "resume_offset": 65536 }
|
||||
```
|
||||
|
||||
`resume_offset` is the number of bytes the receiver already has on disk.
|
||||
When non-zero, the sender seeks to that byte position before streaming.
|
||||
Peers that don't understand this field ignore it and send from the start —
|
||||
the receiver detects this by comparing incoming data to expected offset and
|
||||
will still verify the final SHA-256, but the partial bytes from the interrupted
|
||||
session will be overwritten (YAW/2-only interop degrades gracefully to a full
|
||||
re-download, not corruption).
|
||||
|
||||
### Receiver behaviour
|
||||
|
||||
1. On `file-offer`, the receiver scans its download directory for a `.tmp.meta`
|
||||
sidecar whose `sha256` matches the offer.
|
||||
2. If found, the corresponding `.tmp` file's size is the resume offset. This is
|
||||
sent back in `file-accept`.
|
||||
3. On DC open, the receiver opens the existing `.tmp` in append mode and
|
||||
re-hashes its existing bytes to restore the SHA-256 state.
|
||||
4. On DC close with all bytes received, SHA-256 is verified. Success → sidecar
|
||||
removed, file renamed to final path. Hash mismatch → both files removed.
|
||||
5. On DC close with fewer bytes than expected (interrupted again) → both files
|
||||
kept for the next resume attempt.
|
||||
|
||||
### Sidecar format
|
||||
|
||||
Each in-progress `.tmp` file has a corresponding `.tmp.meta` JSON sidecar:
|
||||
|
||||
```json
|
||||
{ "name": "archive.zip", "sha256": "abc...", "from": "<peer-id>", "size": 1048576 }
|
||||
```
|
||||
|
||||
The sidecar is written when the transfer starts and removed on completion or
|
||||
corruption. Interrupted transfers keep the sidecar indefinitely.
|
||||
|
||||
Reference in New Issue
Block a user