Add invite strings (waste: URI) for peer onboarding

- internal/invite: Encode/Decode waste:<base64json{anchor,network}>
- proto: CmdGenerateInvite + EvtInviteGenerated + InviteString field
- ipc: track active network name; handle generate_invite (returns invite
  string when joined to a network, errors if not joined or no anchor)
- daemon: --join <invite> flag — decodes anchor URL + network name,
  sets anchor and auto-joins on startup
- tui: --join <invite> flag — extracts network name, skips -network
  requirement; ctrl+i generates invite and shows it full-screen;
  Esc dismisses the invite overlay
- FUTURE.md: document multi-network derived-identity design

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Fredrik Johansson
2026-06-21 18:56:24 +02:00
parent 5a0bcee8c6
commit abc6bf46ce
7 changed files with 229 additions and 14 deletions

View File

@@ -0,0 +1,38 @@
package invite
import "testing"
func TestRoundTrip(t *testing.T) {
s, err := Encode("ws://my-vps:17339/ws", "friends")
if err != nil {
t.Fatalf("Encode: %v", err)
}
if len(s) < 7 || s[:6] != "waste:" {
t.Fatalf("expected waste: prefix, got %q", s)
}
inv, err := Decode(s)
if err != nil {
t.Fatalf("Decode: %v", err)
}
if inv.Anchor != "ws://my-vps:17339/ws" {
t.Fatalf("anchor = %q", inv.Anchor)
}
if inv.Network != "friends" {
t.Fatalf("network = %q", inv.Network)
}
}
func TestDecodeInvalid(t *testing.T) {
cases := []string{
"",
"notaninvite",
"waste:!!!",
"waste:" + "eyJhbmNob3IiOiIifQ==", // missing network
}
for _, c := range cases {
if _, err := Decode(c); err == nil {
t.Errorf("Decode(%q) expected error, got nil", c)
}
}
}