diff --git a/FUTURE.md b/FUTURE.md index e54d664..7f93ca9 100644 --- a/FUTURE.md +++ b/FUTURE.md @@ -11,10 +11,10 @@ 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 -- Cryptography and handshake -- NAT traversal and relay fallback -- File transfer +- 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. @@ -23,95 +23,73 @@ Talks to the daemon over the IPC port. The separation means the UI is replaceabl 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 (near-term) -A terminal UI is worth building first, as a `cmd/tui` using [Bubble Tea](https://github.com/charmbracelet/bubbletea). Since the IPC contract is already the full boundary, a TUI is just another client — connect to `127.0.0.1:17337`, receive the `state_snapshot`, then funnel incoming events into Bubble Tea's update loop. Incoming mesh events map naturally onto its Elm-style message model. - -Benefits over jumping straight to a native GUI: -- Works over SSH; zero packaging complexity -- Validates the full IPC protocol and message flow end-to-end -- Useful day-to-day while the native UI is still future work - -The TUI doesn't replace the long-term GUI — it won't serve non-technical friends — but it's the right first UI milestone. +#### 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. --- -## Protocol Modernization +## Protocol -### NAT Traversal -The main unsolved problem from the original WASTE — one party always needed an open port. +### 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. -- Try UDP hole punching (STUN) first -- Fall back to an encrypted relay (DERP-style) when hole punching fails -- Relay sees only opaque encrypted blobs — end-to-end encryption holds -- Run the relay on a Hetzner VPS; `waste-relay` already implements the blind-forward model +The one remaining gap: symmetric-NAT pairs fail without TURN. An optional TURN relay (coturn in relay mode) can recover these. The protocol spec (§11) explicitly leaves this as an opt-in — no relay is the default. -### Bootstrapping & Rendezvous -No DHT needed at small group scale (10–50 nodes). Keep it simple: +### Signaling ✅ YAW/2.1 (shipped) +Forward-secret signaling via per-session ephemeral X25519 keys. See PROTOCOL.md §2.1 and `internal/anchor/client.go`. Falls back transparently to 2.0 static-key sealing for peers that don't speak 2.1. -- Each peer generates an Ed25519 keypair on first run — the public key **is** their identity -- Share a small signed invite file (`.waste-invite`) out of band: email, Signal, whatever -- The invite contains: current IP:port hint + public key + short-lived signature -- Once two peers connect, they gossip each other's addresses to mutual friends -- A local known-peers list in the data directory is sufficient at this scale +### Multi-Network Support ✅ (shipped) +One daemon, multiple simultaneously-joined networks. Per-network HKDF-derived identities prevent cross-network correlation. Network-scoped SQLite stores, IPC commands carry `network_id`. -### Identity -- Persistent Ed25519 keypair, generated once -- Public key = stable identity, not a mutable nickname -- No phone number, no central registry — closer to Signal's model than WASTE's original unregistered aliases +### Invite System ✅ (shipped) +`waste:` URIs encoding anchor URL + network name. `--join` flag on daemon and TUI. Generated via IPC `generate_invite` or `Ctrl+I` in the TUI. + +### File Transfer ✅ (shipped) +Dedicated binary DataChannel per transfer (`f:`). SHA-256 integrity verification. 64 KiB chunks with backpressure. Auto-accept. Downloads to `/downloads-/`. + +--- + +## Remaining Work ### Per-Network Share Directories +The `-share-dir` flag is still global. A peer on two networks shares the same directory with both. The fix: make share dir per-network, configurable via an IPC command `set_share_dir {network_id, path}` or a config file, so Alice can share work files on "work" and music on "friends" independently. -The current `-share-dir` flag is a single global directory. Eventually, each network should have its own independent share set: -- Alice shares `/home/alice/Downloads/work-files` on the "work" network -- Alice shares `/home/alice/Music` on the "friends" network -- Peers on "work" never see Alice's music, and vice versa +### Peer Gossip (§8.4) +The `peer_gossip` wire type exists in proto but is not implemented. When a peer knows about other peers (from prior connections), it could share address hints so the mesh can reconnect without the anchor. This makes the anchor optional once the initial handshake has happened — the network self-heals if the anchor goes down. -When multi-network support lands (see below), the `ShareDir` field on `networkCtx` replaces the current global `Mesh.ShareDir`. The IPC `get_file_list` and daemon `-share-dir` flag should move to be per-network configuration — either via a config file or via an IPC command `set_share_dir` scoped to a `network_id`. +### File Transfer UX +- Manual accept/reject (currently auto-accept) +- Transfer cancellation via `file-cancel` +- Progress display in TUI +- Resume after disconnection -### Multi-Network Support +### Native UI +Web frontend (React) + Tauri/native packaging. The IPC protocol is already the full boundary — the UI is a pure consumer. -A single client should be able to participate in multiple networks simultaneously (e.g. "work" and "friends") without leaking that both identities belong to the same person. - -**Privacy constraint:** if the same Ed25519 keypair is used across networks, any peer who is a member of both networks can trivially correlate you. The anchor also sees the same public key across networks. - -**Solution — per-network derived identities:** -- One master Ed25519 seed in `identity.json` -- Per-network keypair = `HKDF(masterSeed, "yaw2-net", networkHash)` -- Same master + same network name = same derived keypair (stable identity within a network) -- Different networks = different peer IDs; correlation is impossible without knowing both network names -- The anchor sees only the derived public key - -**Daemon changes:** -- Replace the single `networkCancel` with a `map[networkID]*networkCtx` -- Each context holds its own: derived identity, mesh, anchor connection, store (`messages-.db`) -- `join_network` returns a `network_id` token used to scope subsequent commands - -**IPC changes (breaking):** -- All commands and events gain a `network_id` field -- `get_state` returns an array of all joined networks -- `join_network` responds with `network_joined` carrying the derived peer ID for that network - -**TUI changes:** -- Top-level network switcher (e.g. `[work] [friends]`) -- Rooms and peers are scoped per network underneath - -### Transport (Long-term) -Current transport is TCP with custom framing. QUIC is worth revisiting once the core is solid — it gives multiplexing and better NAT traversal behavior essentially for free. +### TURN relay (optional) +For symmetric-NAT pairs. Opt-in, off by default. Operates blind (relay sees only opaque DTLS-encrypted blobs). Run coturn in relay mode on the same VPS as the anchor. --- ## Roadmap -| Priority | Item | +| Status | Item | |---|---| -| 1 | Deploy `waste-relay` to Hetzner; verify cross-internet NAT traversal | -| 2 | Invite file format (`.waste-invite`) — solve bootstrapping without manual IP sharing | -| 3 | Peer gossip — auto-connect to friends-of-friends after initial invite | -| 4 | File transfer — chunked, encrypted, resumable | -| 5 | Message persistence — SQLite via `modernc.org/sqlite` | -| 6 | UI — web frontend consuming the IPC port; native packaging | -| 7 | UDP hole punching — full STUN implementation in `internal/nat` | -| 8 | QUIC transport — replace TCP framing for better NAT behavior | +| ✅ shipped | Daemon + anchor server | +| ✅ shipped | WebRTC DataChannels (ICE/STUN hole punching) | +| ✅ shipped | Ed25519 identity, nacl/box signaling (YAW/2.0) | +| ✅ 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 (dedicated binary DataChannels) | +| ✅ shipped | Forward-secret signaling (YAW/2.1 ephemeral X25519) | +| next | Per-network share directories | +| next | Peer gossip (anchor-free reconnection) | +| next | File transfer UX (manual accept, cancel, TUI progress) | +| future | Native UI (React + Tauri) | +| future | TURN relay (opt-in, for symmetric-NAT pairs) | --- @@ -120,5 +98,5 @@ Current transport is TCP with custom framing. QUIC is worth revisiting once the - **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 relay is dumb infrastructure only +- **Equal nodes** — no peer is "the server"; the anchor is dumb infrastructure - **The soul** — a private overlay for people you actually trust