Files
waste-go/EXTENSIONS.md
Fredrik Johansson 31e13fd509 Add signed invites, hang links, multi-share, and EXTENSIONS.md
- 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>
2026-06-26 22:05:56 +02:00

5.2 KiB

waste-go Protocol Extensions

These are additive extensions to YAW/2 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:

{
  "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:

{
  "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.


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

{"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

{"type":"shares_list","shares":[{"path":"...","networks":["*"]}]}

Persistence

shares.json in the data directory (next to identity.json). Each entry:

{ "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.