Files
waste-go/internal/invite/invite.go

75 lines
2.3 KiB
Go
Raw Normal View History

// Package invite encodes and decodes waste invite strings.
//
// Format: "waste:<url-safe-base64(json)>"
//
// The JSON payload is compatible with yaw2: the `net` field carries the full
// 64-char hex SHA-256("yaw2-net:"+name) hash that yaw2 clients pass directly
// to the signaling server. A yaw2 client that can parse the base64 JSON can join
// the same network without knowing the plaintext name.
package invite
import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
)
const prefix = "waste:"
// Invite holds the information needed to join a network.
type Invite struct {
Anchor string `json:"anchor"` // WebSocket anchor URL
Network string `json:"network"` // plaintext network name
Net string `json:"net,omitempty"` // 64-char hex SHA-256("yaw2-net:"+name) — yaw2 `net` field
}
// Encode returns a waste: invite string for the given anchor URL and network name.
func Encode(anchor, network string) (string, error) {
if anchor == "" {
return "", fmt.Errorf("anchor URL is required")
}
if network == "" {
return "", fmt.Errorf("network name is required")
}
h := sha256.Sum256([]byte("yaw2-net:" + network))
b, err := json.Marshal(Invite{
Anchor: anchor,
Network: network,
Net: hex.EncodeToString(h[:]),
})
if err != nil {
return "", err
}
return prefix + base64.URLEncoding.EncodeToString(b), nil
}
// Decode parses a waste: invite string and returns the Invite.
func Decode(s string) (Invite, error) {
s = strings.TrimSpace(s)
if !strings.HasPrefix(s, prefix) {
return Invite{}, fmt.Errorf("not a waste invite (expected 'waste:' prefix)")
}
b, err := base64.URLEncoding.DecodeString(strings.TrimPrefix(s, prefix))
if err != nil {
return Invite{}, fmt.Errorf("invalid invite: %w", err)
}
var inv Invite
if err := json.Unmarshal(b, &inv); err != nil {
return Invite{}, fmt.Errorf("invalid invite payload: %w", err)
}
if inv.Anchor == "" || inv.Network == "" {
return Invite{}, fmt.Errorf("invite is missing anchor or network name")
}
return inv, nil
}
// NetHash returns the full 64-char hex network hash for the given name
// (SHA-256("yaw2-net:" + name)). This is the `net` field sent to the anchor.
func NetHash(name string) string {
h := sha256.Sum256([]byte("yaw2-net:" + name))
return hex.EncodeToString(h[:])
}