- Signed invites: waste: URI gains inviter+sig fields (Ed25519); hello carries the invite so receiving peers can verify against known keys - RequireInvite per-network flag: rejects peers without valid signed invite - Hash-based hang links: #waste:base64 fragment pre-fills join form without server-side leakage of network name - Multi-share: shares.json (daemon) + waste_shares localStorage (browser); IPC add_share/remove_share/list_shares commands - EXTENSIONS.md: addendum documenting all waste-go protocol deviations from YAW/2; all extensions are additive and backward compatible Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
174 lines
5.2 KiB
Markdown
174 lines
5.2 KiB
Markdown
# waste-go Protocol Extensions
|
|
|
|
These are additive extensions to [YAW/2](PROTOCOL.md) implemented by waste-go.
|
|
They do **not** break compatibility — YAW/2-only peers silently ignore all new
|
|
fields. Where a waste-go peer connects to a YAW/2-only peer, the extension
|
|
simply has no effect on that peer.
|
|
|
|
---
|
|
|
|
## EXT-001 — Signed Invites
|
|
|
|
**Status:** implemented
|
|
**Affects:** `waste:` invite format, `hello` DataChannel message
|
|
|
|
### Motivation
|
|
|
|
The base YAW/2 network model is open to anyone who knows the anchor URL and
|
|
network name (or hash). This extension adds opt-in cryptographic membership
|
|
gating: invites are signed by an existing peer, and peers that enforce
|
|
`RequireInvite` reject hellos that carry no valid signed invite.
|
|
|
|
### Invite format changes
|
|
|
|
The `waste:` invite payload (base64-encoded JSON) gains two optional fields:
|
|
|
|
```json
|
|
{
|
|
"anchor": "wss://...",
|
|
"network": "friends",
|
|
"net": "<64-hex SHA-256(yaw2-net:name)>",
|
|
"inviter": "<64-hex Ed25519 pubkey of signing peer>",
|
|
"sig": "<hex Ed25519 signature>"
|
|
}
|
|
```
|
|
|
|
The signature covers the following bytes (null-separated):
|
|
|
|
```
|
|
anchor \x00 network \x00 net \x00 inviter
|
|
```
|
|
|
|
Unsigned invites (`inviter`/`sig` absent) remain valid for backward compat.
|
|
|
|
### Hello message extension
|
|
|
|
The YAW/2 §6 hello message gains one optional field:
|
|
|
|
```json
|
|
{
|
|
"type": "hello",
|
|
"id": "<hex pubkey>",
|
|
"nick": "alice",
|
|
"caps": ["chat", "file"],
|
|
"sig": "<DTLS binding sig>",
|
|
"invite": "waste:eyJ..."
|
|
}
|
|
```
|
|
|
|
`invite` carries the full `waste:` string the connecting peer used to join.
|
|
YAW/2-only peers ignore this field.
|
|
|
|
### Enforcement
|
|
|
|
Per-network flag `RequireInvite` (set via `join_network` IPC command).
|
|
When enabled:
|
|
|
|
1. A peer that presents no `invite` in hello is disconnected immediately.
|
|
2. A peer that presents an invite with no signature is disconnected.
|
|
3. A peer whose invite signature is invalid is disconnected.
|
|
4. A peer whose invite was signed by an unknown peer ID (not in the store or
|
|
currently connected) is disconnected.
|
|
|
|
The inviter's key must be a **known peer** — i.e. previously connected and
|
|
stored in the per-network SQLite store, or currently connected. This forms a
|
|
chain of trust: Alice (founder) invites Bob; Bob's key is now known; Bob can
|
|
invite Carol, whose invite Alice will also accept.
|
|
|
|
**Default:** off. Networks opt in. Existing networks with no RequireInvite
|
|
behave exactly as before.
|
|
|
|
---
|
|
|
|
## EXT-002 — Hash-based Hang Link
|
|
|
|
**Status:** implemented
|
|
**Affects:** web UI URL handling only, no wire changes
|
|
|
|
### Motivation
|
|
|
|
A shareable URL that pre-fills the join form without conveying cryptographic
|
|
membership. Suitable for public announcements ("come hang out here"). The
|
|
fragment is never sent to the server, keeping the network name opaque to
|
|
server logs and HTTP intermediaries.
|
|
|
|
### Format
|
|
|
|
```
|
|
https://host/#waste:eyJ...
|
|
```
|
|
|
|
The fragment payload is the standard `waste:` base64 JSON with only `network`
|
|
and `anchor` fields — no `inviter`, no `sig`. This does **not** grant
|
|
membership on networks with `RequireInvite` enabled; it only pre-fills the
|
|
join form.
|
|
|
|
The web UI generates hang links via the 🔗 button in the Networks sidebar
|
|
section. Arriving users see the join form pre-populated and still need a
|
|
proper signed invite (if the network enforces it) to be accepted by peers.
|
|
|
|
---
|
|
|
|
## EXT-003 — Multi-Share Configuration
|
|
|
|
**Status:** implemented
|
|
**Affects:** IPC protocol only, no peer-to-peer wire changes
|
|
|
|
### New IPC commands
|
|
|
|
```jsonc
|
|
{"type":"add_share","path":"/home/alice/Music"} // global
|
|
{"type":"add_share","path":"/home/alice/Docs","networks":["abc123"]} // scoped
|
|
{"type":"remove_share","path":"/home/alice/Music"}
|
|
{"type":"list_shares"}
|
|
```
|
|
|
|
### New IPC event
|
|
|
|
```jsonc
|
|
{"type":"shares_list","shares":[{"path":"...","networks":["*"]}]}
|
|
```
|
|
|
|
### Persistence
|
|
|
|
`shares.json` in the data directory (next to `identity.json`). Each entry:
|
|
|
|
```json
|
|
{ "path": "/absolute/path", "networks": ["*"] }
|
|
```
|
|
|
|
`networks: ["*"]` = global (all networks). Specific network IDs = scoped.
|
|
Coexists with the legacy `set_share_dir` single-dir mechanism.
|
|
|
|
File listings returned by `get_file_list` and `MsgFileListReq` include
|
|
entries from all applicable share roots, with relative `path` fields
|
|
(e.g. `"path": "docs/report.pdf"`).
|
|
|
|
---
|
|
|
|
## EXT-004 — TURN Relay (browser mode)
|
|
|
|
**Status:** implemented (browser mode); pending (daemon mode)
|
|
**Affects:** ICE server configuration only, no wire changes
|
|
|
|
The browser adapter reads `WASTE_CONFIG.turnURL` and `WASTE_CONFIG.turnSecret`
|
|
and adds a TURN server to the WebRTC `ICEServers` list. Credentials are
|
|
generated using HMAC-SHA1 of the username (coturn `use-auth-secret` scheme).
|
|
|
|
YAW/2 §0 explicitly declines TURN ("No relay (TURN)"). This extension is
|
|
opt-in via server configuration and does not affect peers that omit it.
|
|
|
|
---
|
|
|
|
## EXT-005 — Per-Network Path in FileEntry
|
|
|
|
**Status:** implemented
|
|
**Affects:** `MsgFileListResp` wire message (additive field)
|
|
|
|
`FileEntry` gains an optional `path` field carrying the file's relative path
|
|
within its share root (e.g. `"docs/report.pdf"`). Peers that don't understand
|
|
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`.
|