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

@@ -11,6 +11,7 @@ import (
"github.com/waste-go/internal/anchor"
"github.com/waste-go/internal/crypto"
"github.com/waste-go/internal/invite"
"github.com/waste-go/internal/ipc"
"github.com/waste-go/internal/mesh"
"github.com/waste-go/internal/proto"
@@ -22,8 +23,21 @@ func main() {
alias := flag.String("alias", "anon", "display name shown to peers (advisory only)")
ipcPort := flag.Int("ipc-port", 17337, "port for local IPC (UI connects here)")
anchorURL := flag.String("anchor", "", "anchor WebSocket URL, e.g. ws://your-vps:17339/ws")
joinInvite := flag.String("join", "", "waste: invite string — sets anchor URL and auto-joins the network on startup")
flag.Parse()
// --join overrides/sets the anchor URL and triggers an auto-join.
var autoJoinNetwork string
if *joinInvite != "" {
inv, err := invite.Decode(*joinInvite)
if err != nil {
log.Fatalf("invalid invite: %v", err)
}
*anchorURL = inv.Anchor
autoJoinNetwork = inv.Network
log.Printf("daemon: invite decoded — anchor=%s network=%s", inv.Anchor, inv.Network)
}
dir := expandHome(*dataDir)
id, err := crypto.LoadOrCreate(dir, *alias)
if err != nil {
@@ -51,7 +65,14 @@ func main() {
log.Printf("daemon: left network %q", networkName)
}
if err := ipc.Run(m, *ipcPort, joinFn); err != nil {
// Auto-join from --join flag before starting IPC (non-blocking).
if autoJoinNetwork != "" {
ctx, cancel := context.WithCancel(context.Background())
_ = cancel // lifecycle managed by the joinFn / ipc.Run leave
go joinFn(ctx, autoJoinNetwork)
}
if err := ipc.Run(m, *ipcPort, *anchorURL, joinFn); err != nil {
log.Fatalf("ipc: %v", err)
}
}