Files
waste-go/FUTURE.md

181 lines
13 KiB
Markdown
Raw Normal View History

# Future Vision
WASTE's design philosophy is still sound: small trusted groups, no central server, encrypted everything, equal nodes. That's essentially what Signal's sealed sender and private groups do today, just without a self-hosted option. This gap is worth filling.
---
## Architecture
Two clean layers, connected by the IPC port.
### Daemon
The real application. A long-running background process that handles everything:
- Peer mesh and connection management (WebRTC DataChannels, DTLS, ICE)
- Cryptography and handshake (Ed25519 identity, nacl/box signaling, YAW/2.1 FS)
- NAT traversal (ICE/STUN via pion/webrtc — no custom relay needed)
- File transfer (dedicated binary DataChannels per transfer)
Exposes a local JSON API over TCP (`127.0.0.1:17337`). Can run headlessly — SSH into a box and the mesh stays alive even with no UI attached.
### UI Layer
Talks to the daemon over the IPC port. The separation means the UI is replaceable without touching the core.
Target: a web frontend (React or similar) wrapped in a native binary using a Tauri-style approach — native packaging, OS webview, no Electron weight. Avoids the wxWidgets ugliness of the old wxWASTE fork and the Qt licensing headaches of the VIA fork.
#### TUI ✅ (shipped)
A terminal UI (`cmd/tui`) using [Bubble Tea](https://github.com/charmbracelet/bubbletea). Three-pane layout: rooms, messages, peers. Supports group chat, DMs, room switching, invite generation. Works over SSH.
2026-06-21 16:20:59 +02:00
#### Web UI ✅ (shipped)
React + Vite frontend. Two modes:
- **Browser mode** — runs entirely in-browser, connects directly to the anchor via WebSocket. No daemon required. Identity persists in `localStorage`. File sharing, file push, per-peer ICE/NAT status.
- **Daemon mode** — web UI connects to a local daemon over WebSocket IPC. Same UI, different adapter.
---
## Protocol
### NAT Traversal ✅ (WebRTC ICE/STUN)
Solved by using WebRTC DataChannels via pion. ICE gathers host + server-reflexive (STUN) candidates and performs UDP hole punching automatically. The anchor (`cmd/anchor`) doubles as a STUN server on UDP/3478.
### TURN relay ✅ (shipped)
Both browser and daemon modes support TURN relay.
**Browser mode:** `iceServers()` in `browser.ts` reads `WASTE_CONFIG.turnURL` and fetches a time-limited credential from the anchor's `GET /turn-credentials` endpoint (HMAC-SHA1, compatible with coturn `use-auth-secret`) rather than holding the shared secret client-side. The peer dot turns yellow for relayed connections (`candidate_type: relay`).
> **Security fix:** earlier this previously embedded `turnSecret` directly in `WASTE_CONFIG`, which let anyone reading the PWA's JS mint unlimited long-lived TURN credentials. The secret now lives only on the anchor (`-turn-secret` flag); the anchor mints short-lived credentials per-request instead.
**Daemon mode:** `-turn-url` and `-turn-secret` flags on `cmd/daemon`. `turnICEServers()` in `internal/netmgr/manager.go` generates HMAC-SHA1 credentials and injects them into the ICE server list for every new peer connection.
### Signaling ✅ YAW/2.1 (shipped)
Forward-secret signaling via per-session ephemeral X25519 keys. Falls back transparently to 2.0 static-key sealing for peers that don't speak 2.1.
### Multi-Network Support ✅ (shipped)
One daemon, multiple simultaneously-joined networks. Per-network HKDF-derived identities prevent cross-network correlation. Network-scoped SQLite stores.
### Invite System ✅ (shipped)
`waste:<base64>` URIs encoding anchor URL + network name. `--join` flag on daemon and TUI. `Ctrl+I` in TUI or `generate_invite` via IPC.
### File Transfer ✅ (shipped)
Dedicated binary DataChannel per transfer (`f:<xid>`). SHA-256 integrity verification. 64 KiB chunks with backpressure. Auto-accept. In browser mode: both pull (browse peer's shared folder) and push (📎 send directly to a peer).
### Peer Gossip ✅ (shipped)
When a new peer connects, the mesh immediately gossips the full peer list to them (`peer_gossip` wire message). New arrivals discover existing peers without needing the anchor to re-introduce them. The anchor becomes optional once the first handshake has happened — the mesh self-heals around anchor downtime.
---
## Remaining Work
### Session Persistence ✅ (shipped)
Browser mode now auto-rejoins on reload. The last-used network name, alias, and anchor URL are saved to `localStorage` on join and restored on load. A ⏻ logout button in the sidebar clears session state (optionally including the identity keypair) and reloads the page.
### Per-Network Share Directories ✅ (shipped)
Share state is tracked per `network_id` in the store (`sharedFilesByNetwork`). Switching networks switches the active share.
### Persistent Multi-Share Configuration ✅ (shipped)
Multiple share roots per network, with global (all networks) or scoped visibility.
- **Daemon:** `shares.json` next to `identity.json` in the data dir. `add_share`/`remove_share`/`list_shares` IPC commands. File listing recursively walks all share roots, returning relative paths. Backward compatible with the existing `set_share_dir` single-dir mechanism.
- **Browser:** `waste_shares` in `localStorage` stores named share records (folder name, global flag). The `ShareManager` sidebar component shows the list with re-pick (↺) and remove (✕) buttons. Actual `File` objects live in memory — the record persists across reloads so the user can restore with one click.
### Additional Channels / Rooms ✅ (shipped)
Custom rooms are supported in both the web UI and the TUI.
**Web UI:** The `+` button in the Rooms sidebar creates custom rooms, stored in `customRooms` keyed by `network_id`. Room names are slugified strings — any peer that sends to a room name causes it to appear on the recipient automatically.
**TUI:** Type `/room <name>` in the input to create a room. The daemon persists it in the `rooms` SQLite table and echoes a `room_created` IPC event back. On reconnect, rooms are restored via `state_snapshot`. Rooms that receive messages while not active show a `*` prefix in the sidebar; the marker clears when you switch to that room.
DM rooms (`dm:<peerId>`) appear automatically in both interfaces when messages arrive.
### File Transfer UX ✅ (shipped)
- Manual accept/reject via the Transfers panel in the sidebar
- Transfer cancellation (`file-cancel` message, closes DataChannel)
- Live progress bar per active transfer
- Push (📎) sends directly to a peer without them needing to share a folder
On daemon start, the download directory is scanned for `.tmp.meta` sidecars and a `resumable_transfers` IPC event is emitted so the UI can show pending transfers with a progress bar.
### Native UI
Web frontend (React, already built) + [Wails v2](https://wails.io) shell for native packaging. Wails is Go-native — no Rust toolchain required. The daemon runs embedded in the same process; the webview connects to the existing WebSocket IPC at `ws://127.0.0.1:17338`. Built in `cmd/app/` via `./build-app.sh`. System tray (Linux/Windows) and OS notifications are implemented. macOS menu-bar tray requires Cocoa main-thread integration — currently a stub.
---
## Roadmap
| Status | Item |
|---|---|
| ✅ shipped | Daemon + anchor server |
| ✅ shipped | WebRTC DataChannels (ICE/STUN hole punching) |
| ✅ shipped | Ed25519 identity, nacl/box signaling (YAW/2.0 + 2.1) |
| ✅ shipped | IPC protocol — join/leave/chat/DM/state |
| ✅ shipped | Message persistence (SQLite, per-network) |
| ✅ shipped | TUI (`cmd/tui`, Bubble Tea) |
| ✅ shipped | Invite system (`waste:` URI, `--join` flag) |
| ✅ shipped | Multi-network support (HKDF derived identities) |
| ✅ shipped | File transfer (binary DataChannels, pull + push) |
| ✅ shipped | Forward-secret signaling (YAW/2.1 ephemeral X25519) |
| ✅ shipped | Peer gossip (anchor-free mesh reconnection) |
| ✅ shipped | Web UI — browser mode + daemon mode |
| ✅ shipped | Per-peer NAT/ICE status, identity backup/restore |
| ✅ shipped | Per-network share directories |
| ✅ shipped | Additional channels/rooms per network |
| ✅ shipped | TURN relay (browser mode, coturn `use-auth-secret`) |
| ✅ shipped | File transfer UX (progress, cancel, manual accept) |
| ✅ shipped | Session persistence + logout (browser mode) |
| ✅ shipped | Persistent multi-share config (shares.json + localStorage) |
| ✅ shipped | Subfolder support + directory browser UI in file browser |
| ✅ shipped | Signed invites + invite-only networks (`RequireInvite`) |
| ✅ shipped | Hash-based "come hang" links (`#waste:...`) |
| ✅ shipped | Protocol extensions documented in EXTENSIONS.md |
| ✅ shipped | TURN relay for daemon mode (`-turn-url` / `-turn-secret`) |
| ✅ shipped | TUI room creation + daemon-side room persistence |
| ✅ shipped | Unread room indicators in TUI (`*` prefix) |
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>
2026-06-28 21:38:28 +02:00
| ✅ shipped | Per-network download directories (`-download-dir` flag + `set_download_dir` IPC) |
| ✅ shipped | File transfer resume after disconnection |
| ✅ shipped | PWA manifest — installable via "Add to Home Screen" on iOS and Android |
| ✅ shipped | Native desktop app (Wails 2) — system tray (Linux/Windows), OS notifications, single binary |
| ✅ shipped | Gitea Actions CI — server binaries (all platforms via cross-compile) + desktop app (Linux amd64) |
| ✅ shipped | File transfer resume UX — resumable transfers surfaced in Transfers panel on reconnect |
| ✅ shipped | P2P message history gossip (EXT-007) — new peers receive recent history from first connected peer |
| ✅ shipped | Date-aware timestamps in TUI and web UI |
| ✅ shipped | Historical peer alias resolution in web UI |
| ✅ shipped | Message reactions (emoji picker, full-stack: wire protocol, SQLite, IPC, web UI, TUI) |
| ✅ shipped | Link rendering + image preview in web UI messages |
| ✅ shipped | Responsive mobile layout (slide-over sidebar, hamburger button) |
| ✅ shipped | TUI multi-network (join/switch networks at runtime, `ctrl+n`, `/join`, `/net`) |
| ✅ shipped | TUI message reactions (`/react <emoji>` or `/react <n> <emoji>`) |
| 🔜 planned | Push notifications (PWA Web Push + service worker) |
### Message Reactions ✅ (shipped)
Full-stack emoji reactions. The web UI shows a `+` button on hover that opens a six-emoji picker (👍 ❤️ 😂 😮 😢 🙏). Reactions render as chips below each message; clicking an existing chip toggles your own reaction. Reactions are stored in SQLite (`reactions` table), gossiped over the mesh as `reaction` wire messages (EXT-008), and replayed via IPC on reconnect.
Browser mode: `browser.ts` independently mirrors the daemon — reactions flow over the DataChannel and are broadcast to all connected peers.
TUI: messages show `[n]` line numbers. Use `/react <emoji>` (reacts to last message) or `/react <n> <emoji>` (reacts to message `n`). Reactions render inline below the target message.
### Link Rendering + Image Preview ✅ (shipped)
URLs in messages are auto-linked. URLs ending in a recognized image extension (`.jpg`, `.png`, `.gif`, `.webp`, `.svg`) render an inline `<img>` preview (max 320×200px). `blob:` and `data:image` URLs are also treated as images.
### Responsive Mobile Layout ✅ (shipped)
At viewport width ≤ 600px the sidebar becomes a fixed-position slide-over drawer, hidden off-screen by default (`transform: translateX(-100%)`). A `☰` hamburger button in the message pane header toggles it open. Clicking any room or network in the sidebar closes it automatically. The layout is dimmed while the sidebar is open via a `::before` overlay.
### Push Notifications (planned)
The web UI is already a PWA (installable, has manifest). The missing half is a service worker + Web Push subscription:
1. **Service worker** — intercepts `push` events and shows OS notifications via `showNotification()`.
2. **VAPID key pair** — generated once by the daemon (`-vapid-key` flag); the public key is served to the browser so it can subscribe.
3. **Subscription persistence** — the browser's `PushSubscription` JSON is sent to the daemon over IPC (`register_push` command). The daemon stores it per-network-per-peer.
4. **Daemon relay** — when a `message_received` event fires with no active IPC WebSocket connection, the daemon POSTs a Web Push notification to the stored subscription endpoint.
This keeps the architecture clean: the daemon already runs in the background; it becomes the notification relay. No third-party push server is required for self-hosted setups (coturn already in use for TURN; a lightweight Web Push POST is similar).
---
## What to Keep from WASTE
- **Small group** — not a public network, not federated, not discoverable
- **No registration** — no phone number, no email, no central service
- **Encrypted everything** — at rest and in transit, end to end
- **Equal nodes** — no peer is "the server"; the anchor is dumb infrastructure
- **The soul** — a private overlay for people you actually trust