Files
waste-go/internal/proto/proto.go

213 lines
7.3 KiB
Go
Raw Normal View History

// Package proto defines all wire types shared between the daemon and relay.
// Everything on the wire is newline-delimited JSON.
// Binary data (keys, signatures, ciphertext) is base64url encoded.
package proto
import "time"
// ── Identity ──────────────────────────────────────────────────────────────────
// PeerID is a peer's stable identity: their Ed25519 public key, base64url encoded.
// This IS the peer — display names are advisory only and unauthenticated.
type PeerID string
// Short returns the first 8 characters, useful for display.
func (p PeerID) Short() string {
if len(p) < 8 {
return string(p)
}
return string(p)[:8]
}
// PeerInfo is a peer's self-description. Included in the Hello handshake.
type PeerInfo struct {
ID PeerID `json:"id"`
Alias string `json:"alias"` // advisory, not authenticated
PublicKey string `json:"public_key"` // Ed25519 pubkey, base64url
CreatedAt time.Time `json:"created_at"`
}
// ── Handshake ─────────────────────────────────────────────────────────────────
// Hello is the first message sent on a new TCP connection.
type Hello struct {
Version int `json:"version"`
Peer PeerInfo `json:"peer"`
EphemeralKey string `json:"ephemeral_key"` // X25519 pubkey, base64url
Signature string `json:"signature"` // Ed25519 sig over ephemeral_key
}
// HelloAck is the response to Hello, completing the handshake.
type HelloAck struct {
Peer PeerInfo `json:"peer"`
EphemeralKey string `json:"ephemeral_key"`
Signature string `json:"signature"`
RelayCapable bool `json:"relay_capable"`
}
// ── Encrypted envelope ────────────────────────────────────────────────────────
// Envelope wraps all post-handshake messages.
// The payload is ChaCha20-Poly1305 encrypted.
type Envelope struct {
Nonce string `json:"nonce"` // 12 bytes, base64url
Payload string `json:"payload"` // ciphertext, base64url
}
// ── Peer-to-peer message types ────────────────────────────────────────────────
// MsgType identifies the kind of peer message inside an Envelope.
type MsgType string
const (
MsgChat MsgType = "chat"
MsgPeerGossip MsgType = "peer_gossip"
MsgFileOffer MsgType = "file_offer"
MsgFileResp MsgType = "file_response"
MsgFileChunk MsgType = "file_chunk"
MsgPing MsgType = "ping"
MsgPong MsgType = "pong"
)
// PeerMessage is the top-level container decoded from inside an Envelope.
type PeerMessage struct {
Type MsgType `json:"type"`
// Only one of these will be set, depending on Type.
Chat *ChatMessage `json:"chat,omitempty"`
Gossip *PeerGossip `json:"gossip,omitempty"`
FileOffer *FileOffer `json:"file_offer,omitempty"`
FileResp *FileResponse `json:"file_response,omitempty"`
FileChunk *FileChunk `json:"file_chunk,omitempty"`
Seq *uint64 `json:"seq,omitempty"` // for ping/pong
}
// ChatMessage is a message to a room or a DM.
type ChatMessage struct {
ID string `json:"id"`
From PeerID `json:"from"`
To *PeerID `json:"to,omitempty"` // nil = broadcast to room
Room string `json:"room"`
Body string `json:"body"`
SentAt time.Time `json:"sent_at"`
}
// PeerGossip shares known peer addresses.
type PeerGossip struct {
Peers []GossipEntry `json:"peers"`
}
// GossipEntry is one peer hint shared via gossip.
type GossipEntry struct {
Peer PeerInfo `json:"peer"`
AddrHint string `json:"addr_hint"` // IP:port, may be behind NAT
LastSeen time.Time `json:"last_seen"`
}
// FileOffer initiates a file transfer.
type FileOffer struct {
TransferID string `json:"transfer_id"`
Filename string `json:"filename"`
SizeBytes int64 `json:"size_bytes"`
SHA256 string `json:"sha256"`
}
// FileResponse accepts or declines a FileOffer.
type FileResponse struct {
TransferID string `json:"transfer_id"`
Accepted bool `json:"accepted"`
}
// FileChunk is one piece of a file transfer.
type FileChunk struct {
TransferID string `json:"transfer_id"`
Seq uint32 `json:"seq"`
Data string `json:"data"` // base64url
IsLast bool `json:"is_last"`
}
// ── Relay protocol ────────────────────────────────────────────────────────────
// RelayMsgType identifies relay wire messages.
type RelayMsgType string
const (
RelayRegister RelayMsgType = "register"
RelayForward RelayMsgType = "forward"
RelayListPeers RelayMsgType = "list_peers"
RelayForwarded RelayMsgType = "forwarded"
RelayPeerList RelayMsgType = "peer_list"
RelayError RelayMsgType = "error"
)
// RelayMessage is used for both client→relay and relay→client.
type RelayMessage struct {
Type RelayMsgType `json:"type"`
// register
Peer *PeerInfo `json:"peer,omitempty"`
Signature string `json:"signature,omitempty"`
// forward / forwarded
To *PeerID `json:"to,omitempty"`
From *PeerID `json:"from,omitempty"`
Envelope *Envelope `json:"envelope,omitempty"`
// peer_list
Peers []GossipEntry `json:"peers,omitempty"`
// error
Message string `json:"message,omitempty"`
}
// ── IPC protocol (daemon ↔ local UI) ─────────────────────────────────────────
// IpcMsgType identifies IPC messages.
type IpcMsgType string
const (
// Commands (UI → daemon)
CmdSendMessage IpcMsgType = "send_message"
CmdConnect IpcMsgType = "connect"
CmdGetState IpcMsgType = "get_state"
CmdSendFile IpcMsgType = "send_file"
// Events (daemon → UI)
EvtMessageReceived IpcMsgType = "message_received"
EvtPeerConnected IpcMsgType = "peer_connected"
EvtPeerDisconnected IpcMsgType = "peer_disconnected"
EvtIncomingFile IpcMsgType = "incoming_file"
EvtFileProgress IpcMsgType = "file_progress"
EvtStateSnapshot IpcMsgType = "state_snapshot"
EvtError IpcMsgType = "error"
)
// IpcMessage covers both commands and events.
type IpcMessage struct {
Type IpcMsgType `json:"type"`
// send_message
Room string `json:"room,omitempty"`
To *PeerID `json:"to,omitempty"`
Body string `json:"body,omitempty"`
// connect
Addr string `json:"addr,omitempty"`
// send_file
Path string `json:"path,omitempty"`
// events
Peer *PeerInfo `json:"peer,omitempty"`
PeerID *PeerID `json:"peer_id,omitempty"`
Message *ChatMessage `json:"message,omitempty"`
Offer *FileOffer `json:"offer,omitempty"`
TransferID string `json:"transfer_id,omitempty"`
BytesReceived int64 `json:"bytes_received,omitempty"`
TotalBytes int64 `json:"total_bytes,omitempty"`
LocalPeer *PeerInfo `json:"local_peer,omitempty"`
ConnectedPeers []PeerInfo `json:"connected_peers,omitempty"`
Rooms []string `json:"rooms,omitempty"`
ErrorMessage string `json:"error_message,omitempty"`
}