// 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"` }