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>
This commit is contained in:
Fredrik Johansson
2026-06-26 22:05:56 +02:00
parent 0e8ddbf4f4
commit 31e13fd509
12 changed files with 382 additions and 37 deletions

View File

@@ -146,7 +146,6 @@ func handleClient(conn net.Conn, mgr *netmgr.Manager) {
case cmd.NetworkName != "":
netID, err = mgr.Join(cmd.NetworkName, cmd.ShareDir)
case len(cmd.NetworkHash) == 64:
// yaw2-compatible: join by full 64-char hex hash (net field)
netID, err = mgr.JoinByHash(cmd.NetworkHash, cmd.ShareDir)
default:
send(errMsg("join_network: network_name or network_hash (64 hex chars) required"))
@@ -156,7 +155,14 @@ func handleClient(conn net.Conn, mgr *netmgr.Manager) {
send(errMsg(fmt.Sprintf("join_network: %v", err)))
continue
}
// network_joined event (with share_dir) is emitted by Manager.Join/JoinByHash.
if n, ok := mgr.Get(netID); ok {
if cmd.RequireInvite {
n.Mesh.RequireInvite = true
}
if cmd.InviteString != "" {
n.Mesh.InviteString = cmd.InviteString
}
}
_ = netID
case proto.CmdLeaveNetwork:
@@ -298,15 +304,15 @@ func handleClient(conn net.Conn, mgr *netmgr.Manager) {
send(errMsg("generate_invite: daemon was started without -anchor flag"))
continue
}
inv, err := invite.Encode(mgr.AnchorURL(), n.Name)
inv, err := invite.EncodeSigned(mgr.AnchorURL(), n.Name, n.Identity)
if err != nil {
send(errMsg(fmt.Sprintf("generate_invite: %v", err)))
continue
}
send(proto.IpcMessage{
Type: proto.EvtInviteGenerated,
NetworkID: n.ID,
InviteString: inv,
Type: proto.EvtInviteGenerated,
NetworkID: n.ID,
InviteGenerated: inv,
})
case proto.CmdSetShareDir: