protocol

package
v0.1.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 18, 2026 License: MPL-2.0 Imports: 20 Imported by: 0

Documentation

Index

Constants

View Source
const (
	CategoryUnknown   uint8 = 0
	CategorySovereign uint8 = 1 // RecType 0x01–0x0F
	CategoryTopic     uint8 = 2 // RecType 0x10–0x1F
	CategoryMailbox   uint8 = 3 // RecType 0x80–0x8F
)

DHT record categories (Phase 4 spec §2.1).

View Source
const (
	RecTypeTopic   uint8 = 0x10
	RecTypeMailbox uint8 = 0x80
)

RecType constants beyond endpoint (Phase 4).

View Source
const (
	// MaxMailboxPayloadCBOR is the spec §4.8 cap on SignedRecord.payload (MailboxPayload CBOR).
	MaxMailboxPayloadCBOR = 512
	// DefaultMailboxTTL is the recommended mailbox record TTL in seconds (spec §4.8).
	DefaultMailboxTTL uint32 = 3600
)
View Source
const (
	MailboxMsgConnectRequest uint8 = 0x01
	MailboxMsgCandidates     uint8 = 0x02
	MailboxMsgText           uint8 = 0x03
)

Mailbox message types (spec §4.3).

View Source
const (
	MsgPing          uint8 = 0x01
	MsgPong          uint8 = 0x02
	MsgFindNode      uint8 = 0x03
	MsgFindNodeResp  uint8 = 0x04
	MsgFindValue     uint8 = 0x05
	MsgFindValueResp uint8 = 0x06
	MsgStore         uint8 = 0x07
	MsgStoreResp     uint8 = 0x08
	MsgNATProbeReq   uint8 = 0x09 // NAT probe request: ask peer to echo to our claimed address
	MsgNATProbeEcho  uint8 = 0x0A // NAT probe echo: sent by responder directly to claimed address
)

Message type IDs (spec §7.4).

View Source
const (

	// RecTypeEndpoint is the Phase 1 endpoint advertisement (spec §7.6).
	RecTypeEndpoint uint8 = 0x01

	// MaxEndpointSignalURLLen caps each URL in EndpointPayload.Signal / Signals.
	MaxEndpointSignalURLLen = 2048
	// MaxSignalURLs caps EndpointPayload.Signals entry count (multi-center signal URLs).
	MaxSignalURLs = 8
	// MaxTurnURLs caps EndpointPayload.Turns entry count (credential-free relay hints for DHT).
	MaxTurnURLs = 16
	// MaxTurnURLEntryLen caps each turn:// string length in Turns.
	MaxTurnURLEntryLen = 512
)
View Source
const (
	NATUnknown uint8 = iota
	NATFullCone
	NATRestricted
	NATPortRestricted
	NATSymmetric
)

NAT types for EndpointPayload (spec §7.6).

View Source
const MaxTopicBriefRunes = 140

MaxTopicBriefRunes is spec §5.3 one-line description limit.

View Source
const MaxTopicPayloadCBOR = 512

MaxTopicPayloadCBOR is the spec §5.9 cap on SignedRecord.payload for RecType 0x10.

View Source
const ProtocolVersion uint8 = 1

ProtocolVersion is the wire protocol version (spec §7.3).

Variables

View Source
var (

	// ErrInvalidMessage is returned for malformed wire data or failed verification.
	ErrInvalidMessage = errors.New("a2al/protocol: invalid message")
	// ErrUnknownMsgType is returned when msg_type is not defined in Phase 1.
	ErrUnknownMsgType = errors.New("a2al/protocol: unknown msg_type")
)
View Source
var (

	// ErrInvalidRecord is returned when structure, signature, or address binding fails.
	ErrInvalidRecord = errors.New("a2al/protocol: invalid record")
	// ErrRecordExpired means now is past Timestamp+TTL (VerifySignedRecord).
	ErrRecordExpired = errors.New("a2al/protocol: record expired")
)

Functions

func EncodeMailboxPayload

func EncodeMailboxPayload(senderAddr, recipientAddr a2al.Address, recipientPub ed25519.PublicKey, msgType uint8, body []byte) ([]byte, error)

EncodeMailboxPayload encrypts (msgType, body) and returns canonical CBOR MailboxPayload.

func FindNodeResponseWireSize

func FindNodeResponseWireSize(resp *BodyFindNodeResp) (int, error)

FindNodeResponseWireSize returns the canonical CBOR size of a FIND_NODE_RESP body (UDP trim, spec §3.7).

func FindValueResponseWireSize

func FindValueResponseWireSize(resp *BodyFindValueResp) (int, error)

FindValueResponseWireSize returns the canonical CBOR size of a FIND_VALUE_RESP body (UDP trim, spec §3.7).

func FormatObservedUDP

func FormatObservedUDP(ip net.IP, port uint16) ([]byte, error)

FormatObservedUDP encodes IP:port to the same wire form as BodyPong.observed_addr.

func IsPublicIP added in v0.1.4

func IsPublicIP(ip net.IP) bool

IsPublicIP reports whether ip is a routeable public WAN address — not loopback, link-local, private, multicast, unspecified, or RFC 6598 CGNAT (100.64/10). Used by both dht and host packages to validate observed / claimed addresses.

func MarshalSignedMessage

func MarshalSignedMessage(hdr Header, body any, priv ed25519.PrivateKey) ([]byte, error)

MarshalSignedMessage canonical-encodes header and body, signs, and returns the outer CBOR bytes.

func MarshalSignedMessageKeyStore

func MarshalSignedMessageKeyStore(hdr Header, body any, ks acrypto.KeyStore, addr a2al.Address) ([]byte, error)

MarshalSignedMessageKeyStore signs with KeyStore (spec: same preimage as MarshalSignedMessage).

func MarshalTopicPayload

func MarshalTopicPayload(p TopicPayload) ([]byte, error)

MarshalTopicPayload canonical-encodes p and enforces size / brief limits. If p.Version == 0, it is treated as 1 for encoding.

func ParseObservedUDP

func ParseObservedUDP(b []byte) (host string, port uint16, ok bool)

ParseObservedUDP decodes wire observed_addr (4+2 IPv4 or 16+2 IPv6, big-endian port).

func RecordCategory

func RecordCategory(recType uint8) uint8

RecordCategory maps RecType to a storage/query policy bucket. Reserved ranges (0x20–0x7F, 0x90–0xFF) return CategoryUnknown and are stored like sovereign (key must be NodeID(Address)) for forward compatibility.

func RecordIsNewer

func RecordIsNewer(a, b SignedRecord) bool

RecordIsNewer reports whether a should replace b (spec: larger seq wins; tie-break by timestamp).

func TopicNodeID

func TopicNodeID(topic string) a2al.NodeID

TopicNodeID returns SHA-256("topic:" || topicUTF8) as the DHT key for Topic rendezvous (Phase 4 / v2 §10.3). The formula is immutable on the wire.

func VerifySignedRecord

func VerifySignedRecord(sr SignedRecord, now time.Time) error

VerifySignedRecord checks cryptographic integrity: signature validity, endpoint payload shape, and expiry. It does NOT enforce pubkey↔address authority (whether the signing key is allowed to publish for the given Address). Authority is a deployment policy; inject it via dht.Config.RecordAuth at the storage layer.

Types

type BodyFindNode

type BodyFindNode struct {
	Target []byte `cbor:"1,keyasint"` // 32 NodeID
}

type BodyFindNodeResp

type BodyFindNodeResp struct {
	Nodes        []NodeInfo `cbor:"1,keyasint"`
	ObservedAddr []byte     `cbor:"2,keyasint"`
}

type BodyFindValue

type BodyFindValue struct {
	Target  []byte `cbor:"1,keyasint"`
	RecType uint8  `cbor:"2,keyasint,omitempty"` // 0 = all types
}

type BodyFindValueResp

type BodyFindValueResp struct {
	Nodes        []NodeInfo     `cbor:"1,keyasint"`
	Record       *SignedRecord  `cbor:"2,keyasint,omitempty"`
	ObservedAddr []byte         `cbor:"3,keyasint"`
	Records      []SignedRecord `cbor:"4,keyasint,omitempty"`
}

type BodyNATProbeEcho added in v0.1.4

type BodyNATProbeEcho struct {
	Token []byte `cbor:"1,keyasint"` // 8-byte nonce echoed from BodyNATProbeReq
}

BodyNATProbeEcho is sent by the responder directly to the requester's claimed address. Token echoes the nonce from the corresponding BodyNATProbeReq.

type BodyNATProbeReq added in v0.1.4

type BodyNATProbeReq struct {
	Token       []byte `cbor:"1,keyasint"` // 8-byte random nonce
	ClaimedAddr []byte `cbor:"2,keyasint"` // wire-encoded UDP address (6 or 18 bytes)
}

BodyNATProbeReq asks the receiver to send a NATProbeEcho directly to ClaimedAddr. Token (8 bytes) correlates the echo back to the requester's wait channel.

type BodyPing

type BodyPing struct {
	Address []byte `cbor:"1,keyasint"` // 21
}

type BodyPong

type BodyPong struct {
	Address      []byte `cbor:"1,keyasint"`
	ObservedAddr []byte `cbor:"2,keyasint"` // 6 or 18
}

type BodyStore

type BodyStore struct {
	Record SignedRecord `cbor:"1,keyasint"`
	Key    []byte       `cbor:"2,keyasint,omitempty"` // 32-byte DHT key; omit = NodeID(Address)
}

type BodyStoreResp

type BodyStoreResp struct {
	Stored bool `cbor:"1,keyasint"`
}

type DecodedMessage

type DecodedMessage struct {
	Header       Header
	SenderPubkey ed25519.PublicKey
	SenderAddr   a2al.Address
	Body         any
}

DecodedMessage is a verified, parsed wire message.

func VerifyAndDecode

func VerifyAndDecode(raw []byte) (*DecodedMessage, error)

VerifyAndDecode parses the outer message, re-canonicalizes header/body, verifies Ed25519, checks sender_pubkey matches body.address for PING and PONG (spec §Step 3).

type DiscoverFilter

type DiscoverFilter struct {
	Protocols []string `json:"protocols,omitempty"`
	Tags      []string `json:"tags,omitempty"`
}

DiscoverFilter is optional client-side filtering (spec §5.5).

type EndpointPayload

type EndpointPayload struct {
	Endpoints []string `cbor:"1,keyasint"`
	NatType   uint8    `cbor:"2,keyasint"`
	// Signal is the primary WebSocket base URL for ICE trickle signaling (no query; room is
	// appended by peers). Kept for backward compatibility with nodes that only read key 3.
	Signal string `cbor:"3,keyasint,omitempty"`
	// Turns lists optional turn:// URLs without credentials (public relay hints).
	Turns []string `cbor:"4,keyasint,omitempty"`
	// Signals lists additional WebSocket base URLs for ICE signaling (multi-center).
	// New nodes prefer this list; old nodes that only read Signal (key 3) still work.
	// When non-empty, Signal should equal Signals[0] for maximum compatibility.
	Signals []string `cbor:"5,keyasint,omitempty"`
}

EndpointPayload is the CBOR inside SignedRecord.payload for rec_type=0x01 (spec §7.6).

type EndpointRecord

type EndpointRecord struct {
	Address   a2al.Address
	Endpoints []string
	NatType   uint8
	Signal    string
	Turns     []string
	// Signals is the multi-center signal URL list from EndpointPayload.Signals (key 5).
	// When non-empty, callers should prefer this over Signal. Signal is kept for compat.
	Signals   []string
	Timestamp uint64
	Seq       uint64
	TTL       uint32
}

EndpointRecord is the decoded logical view (spec Step 4); no signature material.

func ParseEndpointRecord

func ParseEndpointRecord(sr SignedRecord) (EndpointRecord, error)

ParseEndpointRecord decodes an endpoint record after verification.

type Header struct {
	Version  uint8  `cbor:"1,keyasint"`
	Features uint16 `cbor:"2,keyasint"`
	MsgType  uint8  `cbor:"3,keyasint"`
	TxID     []byte `cbor:"4,keyasint"`
}

Header is the CBOR map in field 1 of the outer message (spec §7.3).

type MailboxMessage

type MailboxMessage struct {
	Sender  a2al.Address
	MsgType uint8
	Body    []byte
	// Seq is copied from the outer SignedRecord.Seq; it is monotonically
	// increasing per sender and serves as a unique record identifier.
	Seq uint64
}

MailboxMessage is one decrypted mailbox entry for the application (spec §4.6).

func OpenMailboxRecord

func OpenMailboxRecord(recipientPriv ed25519.PrivateKey, recipientAddr a2al.Address, sr SignedRecord) (MailboxMessage, error)

OpenMailboxRecord decrypts a verified mailbox SignedRecord for recipientAddr.

type MailboxPayload

type MailboxPayload struct {
	Recipient   []byte `cbor:"1,keyasint"`
	SenderAddr  []byte `cbor:"2,keyasint"`
	EphemeralPK []byte `cbor:"3,keyasint"`
	Ciphertext  []byte `cbor:"4,keyasint"`
	Nonce       []byte `cbor:"5,keyasint"`
}

MailboxPayload is the CBOR inside SignedRecord.payload for rec_type=0x80 (spec §4.1).

type NodeInfo

type NodeInfo struct {
	Address []byte `cbor:"1,keyasint"` // 21 bytes
	NodeID  []byte `cbor:"2,keyasint"` // 32 bytes
	IP      []byte `cbor:"3,keyasint"` // 4 or 16 bytes
	Port    uint16 `cbor:"4,keyasint"`
}

NodeInfo is the DHT routing contact shape (spec §7.6).

type SignedRecord

type SignedRecord struct {
	Address    []byte `cbor:"1,keyasint"`
	RecType    uint8  `cbor:"2,keyasint"`
	Payload    []byte `cbor:"3,keyasint"`
	Seq        uint64 `cbor:"4,keyasint"`
	Timestamp  uint64 `cbor:"5,keyasint"`
	TTL        uint32 `cbor:"6,keyasint"`
	Pubkey     []byte `cbor:"7,keyasint"`
	Signature  []byte `cbor:"8,keyasint"`
	Delegation []byte `cbor:"9,keyasint,omitempty"`
}

SignedRecord is the on-wire record container (spec §7.6). Record signing uses prefix "a2al-rec\0" over CBOR of fields 1–6 (implemented in record.go, Step 4). Field 9 (Delegation) is optional and carries a CBOR-encoded DelegationProof when the signing key (Pubkey) is an operational key publishing on behalf of a master-derived Address.

func SignEndpointRecord

func SignEndpointRecord(priv ed25519.PrivateKey, addr a2al.Address, ep EndpointPayload, seq, timestamp uint64, ttl uint32) (SignedRecord, error)

SignEndpointRecord builds a SignedRecord for a self-signed endpoint advertisement. The signing key must derive the same Address as addr (Phase 1/2 path).

func SignEndpointRecordDelegated

func SignEndpointRecordDelegated(opPriv ed25519.PrivateKey, delegationCBOR []byte, addr a2al.Address, ep EndpointPayload, seq, timestamp uint64, ttl uint32) (SignedRecord, error)

SignEndpointRecordDelegated builds a SignedRecord where an operational key signs on behalf of a master-derived AID (Phase 3 delegation path). delegationCBOR is embedded verbatim in the record and will be verified by receivers via the RecordAuth policy injected into the DHT store. Callers must ensure delegationCBOR is a valid DelegationProof for addr authorized to opPriv.

func SignRecord

func SignRecord(priv ed25519.PrivateKey, addr a2al.Address, recType uint8, payload []byte, seq, timestamp uint64, ttl uint32) (SignedRecord, error)

SignRecord builds a signed record for RecType sovereign custom (0x02–0x0F), topic (0x10–0x1F), or mailbox (0x80–0x8F). payload must be CBOR-encoded bytes.

func SignRecordDelegated

func SignRecordDelegated(opPriv ed25519.PrivateKey, delegationCBOR []byte, addr a2al.Address, recType uint8, payload []byte, seq, timestamp uint64, ttl uint32) (SignedRecord, error)

SignRecordDelegated is SignRecord with an operational key and DelegationProof.

type TopicEntry

type TopicEntry struct {
	Address   a2al.Address
	Seq       uint64
	Timestamp uint64
	TTL       uint32
	Version   uint8
	Topic     string
	Name      string
	Protocols []string
	Tags      []string
	Brief     string
	Meta      map[string]any
}

TopicEntry is the decoded logical view for discovery (spec §5.7), like EndpointRecord.

func FilterTopicEntries

func FilterTopicEntries(in []TopicEntry, f *DiscoverFilter) []TopicEntry

FilterTopicEntries applies protocol/tag filters (AND: all filter tags must appear in entry.Tags; at least one protocol match if filter protocols non-empty).

func TopicEntryFromSignedRecord

func TopicEntryFromSignedRecord(sr SignedRecord) (TopicEntry, error)

TopicEntryFromSignedRecord builds TopicEntry after caller verifies signature / expiry.

type TopicPayload

type TopicPayload struct {
	Version   uint8          `cbor:"1,keyasint"`
	Topic     string         `cbor:"2,keyasint"`
	Name      string         `cbor:"3,keyasint"`
	Protocols []string       `cbor:"4,keyasint"`
	Tags      []string       `cbor:"5,keyasint"`
	Brief     string         `cbor:"6,keyasint"`
	Meta      map[string]any `cbor:"7,keyasint,omitempty"`
}

TopicPayload is the CBOR inside SignedRecord.payload for rec_type=0x10 (spec §5.3).

func ParseTopicRecord

func ParseTopicRecord(sr SignedRecord) (TopicPayload, error)

ParseTopicRecord decodes a topic SignedRecord payload.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL