# waste-go A modern reimagining of [WASTE](https://en.wikipedia.org/wiki/WASTE) — decentralized, friend-to-friend encrypted mesh networking with chat and file sharing. Written in Go. ## Project layout ``` waste-go/ ├── cmd/ │ ├── daemon/ The peer process — run one on each friend's machine │ └── anchor/ WebSocket signaling server — run this on your Hetzner VPS └── internal/ ├── proto/ All wire types (shared by daemon and anchor) ├── crypto/ Ed25519 identity, nacl/box signaling, ChaCha20-Poly1305 ���── mesh/ Connected peer state + DataChannel helpers ├── anchor/ Anchor client — WebRTC signaling via the anchor server └── ipc/ Local JSON API (UI talks to daemon here, port 17337) ``` ## Prerequisites - Go 1.24+ → https://go.dev/dl/ - VS Code with the Go extension (`golang.go`) On first open VS Code will prompt you to install `gopls`, `dlv`, and `goimports` — accept all of them. ## Getting started ```bash # Fetch dependencies go mod tidy # Build everything (confirms it compiles) go build ./... # Terminal 1 — anchor (required for peers to find each other) go run ./cmd/anchor -bind 127.0.0.1:17339 # Terminal 2 — peer A go run ./cmd/daemon -alias alice -data-dir /tmp/waste-alice -ipc-port 17337 -anchor ws://127.0.0.1:17339/ws # Terminal 3 — peer B go run ./cmd/daemon -alias bob -data-dir /tmp/waste-bob -ipc-port 17341 -anchor ws://127.0.0.1:17339/ws ``` Both peers join the same named network via IPC: ```bash # Join peer A to a network called "friends" echo '{"type":"join_network","network_name":"friends"}' | nc 127.0.0.1 17337 # Join peer B to the same network echo '{"type":"join_network","network_name":"friends"}' | nc 127.0.0.1 17341 # Subscribe to peer A's events (in a separate terminal) nc 127.0.0.1 17337 & # Send a message from B echo '{"type":"send_message","room":"general","body":"hello from bob"}' | nc 127.0.0.1 17341 ``` **On Windows** — use PowerShell's built-in TCP client instead of `nc`: ```powershell $c = [System.Net.Sockets.TcpClient]::new('127.0.0.1', 17341) $w = [System.IO.StreamWriter]::new($c.GetStream()); $w.AutoFlush = $true $w.WriteLine('{"type":"join_network","network_name":"friends"}') $w.WriteLine('{"type":"send_message","room":"general","body":"hello from bob"}') # In a separate terminal — subscribe to peer A's events $r = [System.Net.Sockets.TcpClient]::new('127.0.0.1', 17337) $reader = [System.IO.StreamReader]::new($r.GetStream()) while ($true) { $reader.ReadLine() } ``` ## Deploying the anchor on your Hetzner VPS ```bash GOOS=linux GOARCH=amd64 go build -o bin/waste-anchor ./cmd/anchor scp bin/waste-anchor user@your-vps:~/ # On the VPS (also run coturn in STUN-only mode on port 3478) ./waste-anchor -bind 0.0.0.0:17339 ``` Then start daemons with `-anchor ws://your-vps-ip:17339/ws` and they'll connect via WebRTC with ICE (STUN-assisted hole punching) through the anchor for signaling. ## IPC protocol (plain JSON over TCP) Everything is newline-delimited JSON. You can test with `nc 127.0.0.1 17337`. **Commands you send:** ```jsonc {"type":"join_network","network_name":"friends"} {"type":"leave_network"} {"type":"send_message","room":"general","body":"hi"} {"type":"get_state"} ``` **Events the daemon pushes:** ```jsonc {"type":"state_snapshot","local_peer":{...},"connected_peers":[...]} {"type":"peer_connected","peer":{...}} {"type":"session_ready","peer_id":"","nick":"alice"} {"type":"message_received","message":{"from":"","body":"hi","room":"general"}} {"type":"peer_disconnected","peer_id":""} ``` ## Crypto choices | Purpose | Algorithm | Notes | |---|---|---| | Identity | Ed25519 | Fast, small keys, standard | | Peer ID | Hex-encoded Ed25519 pubkey | 64 lowercase hex chars (YAW/2 §2) | | Signaling encryption | XSalsa20-Poly1305 (`nacl/box`) | X25519 keys derived from Ed25519 identity (YAW/2 §3) | | Transport | WebRTC DataChannels (DTLS+SCTP) | pion/webrtc — ICE, hole punching included | | Hashing | SHA-256 | File integrity, network name hashing | Replaces WASTE's original Blowfish/PCBC (broken cipher mode) + RSA. > Peer IDs are 64-char lowercase hex (Ed25519 public key). Existing `identity.json` files > on disk are unaffected — only the over-the-wire representation changed from base64url. ## Testing A self-contained test script boots anchor + three peers, joins them to a named network, exchanges group messages and DMs, and verifies SQLite persistence: ```bash ./test-network.sh ``` Data lands at `/tmp/waste-test` (wiped on each run). Inspect after a run: ```bash sqlite3 /tmp/waste-test/alice/messages.db .headers on SELECT room, from_peer, body, sent_at FROM messages; SELECT peer_id, alias, last_seen FROM peers; ``` ## Roadmap - [x] **Crypto layer** — hex peer IDs, `nacl/box` signaling, Ed25519→X25519 key derivation - [x] **Proto additions** — `mid` dedup field, signaling types, anchor wire types, `hello` message - [x] **Anchor server** (`cmd/anchor`) — WebSocket signaling server replacing TCP relay - [x] **WebRTC peer connections** — pion/webrtc DataChannels; ICE hole-punching via STUN - [x] **Anchor client** (`internal/anchor`) — offer/answer/candidate lifecycle, `nacl/box` sealing - [x] **IPC updates** — `join_network`/`leave_network`; `session_ready` event; DMs via `to` field - [x] **Message persistence** — SQLite (`internal/store`); messages and peer alias cache - [ ] **TUI** — Bubble Tea terminal UI consuming the IPC port - [ ] **File transfer** — chunked binary DataChannel (`f:`) - [ ] **Native UI** — web frontend with native packaging (Tauri-style)