Documentation
¶
Overview ¶
Package meshcore implements the MeshCore mesh protocol: packet encoding and decoding, node identities, path handling, routing types, deduplication rules, the text, group and admin payload codecs, node discovery, and Cayenne LPP sensor telemetry. It contains protocol primitives only — stateless codecs, no radio drivers, no transport, and no stateful application logic (a node's seen-packet cache, for one, belongs to the node, not here).
This is an independent implementation. It is not affiliated with or endorsed by the MeshCore project.
The canonical import path is meshrunner.dev/pkg/meshcore; the code is hosted at github.com/meshrunner-dev/meshcore-go.
Index ¶
- Constants
- Variables
- func AckCRC(message, senderPubKey []byte) uint32
- func AnonPassword(plain []byte) (uint32, string, error)
- func BuildGroupData(dataType uint16, data []byte) ([]byte, error)
- func BuildGroupText(sentAt time.Time, sender, text string) []byte
- func BuildSignedTextPlaintext(sentAt time.Time, authorPrefix []byte, text string, attempt uint8) ([]byte, error)
- func BuildTextAckBody(plain, ackPubKey []byte) ([]byte, error)
- func BuildTextPlaintext(sentAt time.Time, txtType uint8, text string) []byte
- func BuildTextPlaintextAttempt(sentAt time.Time, txtType uint8, text string, attempt int) []byte
- func CString(b []byte) string
- func DecodeSNR(b byte) float64
- func Decrypt(sharedSecret, ciphertext []byte) ([]byte, error)
- func EncodeSNR(dB float64) byte
- func Encrypt(sharedSecret, plaintext []byte) ([]byte, error)
- func EncryptThenMAC(sharedSecret, plaintext []byte) ([]byte, error)
- func FrameAccessList(entries []AccessEntry) []byte
- func FrameAccessListRequest() []byte
- func FrameAdmin(timestamp uint32, body []byte) []byte
- func FrameAnonReply(clock uint32, text string) []byte
- func FrameAnonRequest(timestamp uint32, kind uint8, pathLen uint8, path []byte) ([]byte, error)
- func FrameKeepAliveAck(crc uint32, unsynced uint8) []byte
- func FrameKeepAliveRequest(syncSince uint32) []byte
- func FrameLoginReply(r LoginReply) ([]byte, error)
- func FrameNeighbours(total int, entries []NeighbourEntry) []byte
- func FrameNeighboursQuery(q NeighboursQuery) ([]byte, error)
- func FrameStatusRequest() ([]byte, error)
- func FrameTelemetryRequest(inversePermissions uint8) ([]byte, error)
- func KeepAliveAckCRC(plain, clientPubKey []byte) (uint32, error)
- func MACThenDecrypt(sharedSecret, macAndCiphertext []byte) ([]byte, error)
- func MakeHeader(route RouteType, ptype PayloadType, ver PayloadVersion) uint8
- func MatchScope(p *Packet, keys []TransportKey) int
- func ParseAccessListRequest(body []byte) error
- func ParseAck(payload []byte) (uint32, error)
- func ParseKeepAliveAck(payload []byte) (crc uint32, unsynced uint8, err error)
- func ParseKeepAliveRequest(body []byte) (uint32, error)
- func ParseMultiAck(payload []byte) (crc uint32, remaining uint8, err error)
- func ParsePrivateKey(key []byte) (*LocalIdentity, KeyFormat, error)
- func PathReturnBodyBudget(pathBytes int) int
- func PubKeyPrefixMatcher(hexPrefix string) (func(pub []byte) bool, error)
- func ResponseBodyBudget() int
- func Role(perms uint8) uint8
- func ScopeNames(text string) []string
- func SealedBudget(envelopeLen int) int
- func TextAckPreimage(plain []byte) ([]byte, error)
- func UnframeAdmin(plain []byte) (uint32, []byte, error)
- func ValidPathLen(pathLen uint8) bool
- type AccessEntry
- type AdvTypeFilter
- type Advert
- type AdvertData
- type AnonDatagram
- type AnonReply
- type AnonRequest
- type Datagram
- type DiscoverReq
- type DiscoverResp
- type GroupChannel
- type GroupDataPlaintext
- type GroupDatagram
- type GroupTextPlaintext
- type Identity
- type KeyFormat
- type LPPAccelValue
- type LPPColourValue
- type LPPCoordinate
- type LPPEncoder
- type LPPGPSValue
- type LPPGyroValue
- type LPPPolylineValue
- type LPPReading
- type LocalIdentity
- func (li *LocalIdentity) FirmwareImportable() bool
- func (li LocalIdentity) GoString() string
- func (li *LocalIdentity) PrvKey() []byte
- func (li *LocalIdentity) Seed() []byte
- func (li *LocalIdentity) SharedSecret(otherPub []byte) ([]byte, error)
- func (li *LocalIdentity) Sign(message []byte) []byte
- func (li *LocalIdentity) StdPrivateKey() (ed25519.PrivateKey, bool)
- func (li LocalIdentity) String() string
- type LoginReply
- type Multipart
- type NeighbourEntry
- type NeighboursQuery
- type NeighboursReply
- type Packet
- func BuildAck(ackCRC []byte) (*Packet, error)
- func BuildAdvert(id *LocalIdentity, emittedAt time.Time, app *AdvertData) (*Packet, error)
- func BuildAnonDatagram(destHash, senderPubKey, secret, data []byte) (*Packet, error)
- func BuildCommandAck(plain, senderPubKey []byte) (*Packet, error)
- func BuildControl(data []byte) (*Packet, error)
- func BuildDatagram(ptype PayloadType, destHash, srcHash, secret, data []byte) (*Packet, error)
- func BuildDiscoverReq(req DiscoverReq) (*Packet, error)
- func BuildDiscoverResp(resp DiscoverResp, prefixOnly bool) (*Packet, error)
- func BuildGroupDatagram(ptype PayloadType, ch *GroupChannel, data []byte) (*Packet, error)
- func BuildLoginReq(id *LocalIdentity, repeaterPub []byte, timestamp uint32, password string) (*Packet, []byte, error)
- func BuildMultiAck(ackCRC []byte, remaining uint8) (*Packet, error)
- func BuildPathReturn(destHash, srcHash, secret []byte, pathLen uint8, path []byte, extraType uint8, ...) (*Packet, error)
- func BuildRawCustom(data []byte) (*Packet, error)
- func BuildRequest(id *LocalIdentity, destPub, secret []byte, timestamp uint32, body []byte) (*Packet, error)
- func BuildResponse(destHash, srcHash, secret []byte, timestamp uint32, body []byte) (*Packet, error)
- func BuildRoomLoginReq(id *LocalIdentity, roomPub []byte, timestamp, syncSince uint32, ...) (*Packet, []byte, error)
- func BuildTextAck(message, senderPubKey []byte) (*Packet, error)
- func BuildTrace(tag, authCode uint32, flags uint8) (*Packet, error)
- func ParsePacket(src []byte) (*Packet, error)
- func (p *Packet) AppendPathHash(hash []byte) error
- func (p *Packet) AppendTo(dst []byte) ([]byte, error)
- func (p *Packet) AppendTraceHop(snr float64) error
- func (p *Packet) ConsumeNextHop() ([]byte, error)
- func (p *Packet) Dump() string
- func (p *Packet) HasTransportCodes() bool
- func (p *Packet) Hash() [MaxHashSize]byte
- func (p *Packet) InheritRouting(src *Packet)
- func (p *Packet) IsRouteDirect() bool
- func (p *Packet) IsRouteFlood() bool
- func (p *Packet) MarshalBinary() ([]byte, error)
- func (p *Packet) PathByteLen() int
- func (p *Packet) PathHashCount() int
- func (p *Packet) PathHashSize() int
- func (p *Packet) PayloadType() PayloadType
- func (p *Packet) PayloadVer() PayloadVersion
- func (p *Packet) RawHex() string
- func (p *Packet) RawLength() int
- func (p *Packet) Route() RouteType
- func (p *Packet) SetPathHashCount(n int)
- func (p *Packet) SetPathHashSizeAndCount(size, count int)
- func (p *Packet) String() string
- func (p *Packet) Summary() string
- func (p *Packet) UnmarshalBinary(src []byte) error
- func (p *Packet) Unscope()
- func (p *Packet) UnwrapMultipart() (*Multipart, *Packet, error)
- type PathReturn
- type PayloadType
- type PayloadVersion
- type Region
- type RegionDefError
- type RegionFlags
- type RegionLoader
- type RegionMap
- func (m *RegionMap) ApplyDef(payload string) error
- func (m *RegionMap) Count() int
- func (m *RegionMap) Default() *Region
- func (m *RegionMap) DefaultID() uint16
- func (m *RegionMap) Entries() []Region
- func (m *RegionMap) ExportNames(mask RegionFlags, invert bool, budget int) string
- func (m *RegionMap) ExportTree() string
- func (m *RegionMap) FindByID(id uint16) *Region
- func (m *RegionMap) FindByName(name string) *Region
- func (m *RegionMap) FindByPrefix(prefix string) *Region
- func (m *RegionMap) FindMatch(p *Packet, mask RegionFlags) *Region
- func (m *RegionMap) Home() *Region
- func (m *RegionMap) HomeID() uint16
- func (m *RegionMap) KeysFor(r *Region) []TransportKey
- func (m *RegionMap) NextID() uint16
- func (m *RegionMap) Put(name string, parentID uint16) (*Region, error)
- func (m *RegionMap) Remove(name string) error
- func (m *RegionMap) SetDefault(r *Region)
- func (m *RegionMap) SetHome(r *Region)
- func (m *RegionMap) StartLoad() *RegionLoader
- func (m *RegionMap) Wildcard() *Region
- type RepeaterStats
- type RoomLogin
- type RoomStats
- type RouteType
- type TextPlaintext
- type Trace
- type TransportKey
Constants ¶
const ( ReqTypeGetStatus = 0x01 ReqTypeKeepAlive = 0x02 )
Admin request types (the first body byte after the timestamp). A login request instead carries a password string (or nothing).
const ( AdvTypeNone = 0 AdvTypeChat = 1 AdvTypeRepeater = 2 AdvTypeRoom = 3 AdvTypeSensor = 4 )
Advert application-data flags. The low nibble of the first byte is the node type; the high nibble flags optional fields. Reference: MeshCore src/helpers/AdvertDataHelpers.{h,cpp}.
const ( LPPDigitalInput byte = 0 LPPDigitalOutput byte = 1 LPPAnalogInput byte = 2 LPPAnalogOutput byte = 3 LPPGenericSensor byte = 100 LPPLuminosity byte = 101 LPPPresence byte = 102 LPPTemperature byte = 103 LPPRelativeHumidity byte = 104 LPPAccelerometer byte = 113 LPPBarometricPressure byte = 115 LPPVoltage byte = 116 LPPCurrent byte = 117 LPPFrequency byte = 118 LPPPercentage byte = 120 LPPAltitude byte = 121 LPPConcentration byte = 125 LPPPower byte = 128 LPPDistance byte = 130 LPPEnergy byte = 131 LPPDirection byte = 132 LPPUnixTime byte = 133 LPPGyrometer byte = 134 LPPColour byte = 135 LPPGPS byte = 136 LPPSwitch byte = 142 LPPPolyline byte = 240 )
LPP data types (ElectronicCats / IPSO codes).
const ( // MaxHashSize is the truncated length, in bytes, of a packet hash. MaxHashSize = 8 // MaxPacketPayload is the largest payload a packet may carry. MaxPacketPayload = 184 // MaxPathSize is the largest path field, in bytes (hashes × hash size). MaxPathSize = 64 // MaxTransUnit is the largest encoded packet the radio layer accepts. MaxTransUnit = 255 )
Wire-format limits. Reference: MeshCore src/MeshCore.h.
const ( // PubKeySize is the Ed25519 public key length. PubKeySize = 32 // PrvKeySize is the expanded private key length: the clamped // scalar followed by the signing prefix (orlp/ed25519 layout, as // stored and exchanged by the reference firmware). PrvKeySize = 64 // SeedSize is the private key seed length. SeedSize = 32 // SignatureSize is the Ed25519 signature length. SignatureSize = 64 // MaxAdvertDataSize bounds the application data in an ADVERT. MaxAdvertDataSize = 32 // CipherKeySize is the AES key length: the first half of a // 32-byte shared secret. CipherKeySize = 16 // CipherBlockSize is the AES block length. CipherBlockSize = 16 // CipherMACSize is the truncated HMAC-SHA256 length prepended to // ciphertext (protocol V1). CipherMACSize = 2 // PathHashSize is the node-hash prefix length used by payload // envelopes (protocol V1). The packet path may use wider hashes; // this constant governs the dest/src bytes inside payloads. PathHashSize = 1 )
Identity and cipher sizes. Reference: MeshCore src/MeshCore.h.
const ( AnonReqScopes uint8 = 0x01 AnonReqOwner uint8 = 0x02 AnonReqClock uint8 = 0x03 )
The anonymous questions, which need no session. The reference calls the first one REGIONS; this library says scope for the agreement and keeps region for a radio band.
const ( ReqGetStatus uint8 = 0x01 ReqKeepAlive uint8 = 0x02 ReqGetTelemetry uint8 = 0x03 ReqGetAccessList uint8 = 0x05 ReqGetNeighbours uint8 = 0x06 ReqGetOwnerInfo uint8 = 0x07 )
The authenticated questions, which need one.
const ( PermRoleMask uint8 = 0x03 PermGuest uint8 = 0x00 PermReadOnly uint8 = 0x01 PermReadWrite uint8 = 0x02 PermAdmin uint8 = 0x03 )
The roles a server grants its clients — the reference's PERM_ACL_* in ClientACL.h, living in the low two bits of the permission byte. Guest is the zero role and the one no server persists: granting it is how an entry is removed. Admin is admin at exactly three, never at "non-zero". The upper bits are the server's own (telemetry permissions on the reference) and survive a re-login untouched.
const ( NeighboursNewestFirst uint8 = 0 NeighboursOldestFirst uint8 = 1 NeighboursStrongestFirst uint8 = 2 NeighboursWeakestFirst uint8 = 3 )
The orderings a neighbours query may ask for, as the reference numbers them.
const ( TxtTypePlain uint8 = 0 // a plain text message TxtTypeCLIData uint8 = 1 // CLI output TxtTypeSignedPlain uint8 = 2 // plain text, signed by the sender TxtTypeCLICommand uint8 = 3 // an explicit CLI command )
Text message subtypes (the flags byte's upper six bits).
const AccessKeyPrefixSize = 6
AccessKeyPrefixSize is how much of a member's key an access-list row carries: enough to tell members apart, not enough to address one.
const AdminTagSize = 4
AdminTagSize is the timestamp every admin request and response carries ahead of its body.
const LoginOK uint8 = 0
LoginOK is the verdict byte a successful login reply opens with.
const MaxRegionEntries = 32
MaxRegionEntries bounds the table, wildcard excluded (MAX_REGION_ENTRIES).
const NeighboursMaxBody = 130
NeighboursMaxBody bounds the rows a reply may carry, the reference's own results buffer.
const PublicChannelPSKBase64 = "izOH6cXN6mrJ5e26oRXNcg=="
PublicChannelPSKBase64 is the well-known pre-shared key of the "Public" channel that ships pre-configured on every MeshCore node.
const RepeaterStatsLen = 56
RepeaterStatsLen is the packed length the reference's struct occupies.
const RespServerLoginOK = 0x00
RespServerLoginOK is the first body byte of a successful login reply.
const RoomStatsLen = 52
RoomStatsLen is the packed length the reference's struct occupies.
SharedSecretSize is the required length of every secret passed to the symmetric primitives. The reference always keys them with a 32-byte secret: the cipher uses the first CipherKeySize bytes, the MAC keys HMAC over all of it (PUB_KEY_SIZE in Utils.cpp). Passing a shorter secret — a raw 16-byte channel PSK, say — would silently compute a MAC the firmware rejects, so the primitives require exactly this.
Variables ¶
var ( // ErrBadLPP reports a malformed Cayenne LPP buffer. ErrBadLPP = errors.New("meshcore: malformed Cayenne LPP") // ErrBadLPPValue reports a reading whose value does not match its type. ErrBadLPPValue = errors.New("meshcore: Cayenne LPP value type mismatch") // ErrLPPFull reports a reading refused whole because it would not // fit the encoder's limit; the buffer holds only whole records. ErrLPPFull = errors.New("meshcore: Cayenne LPP buffer full") )
Codec errors.
var ( ErrNotControl = errors.New("meshcore: not a CONTROL packet") ErrNotDiscoverReq = errors.New("meshcore: not a discovery request") ErrNotDiscoverResp = errors.New("meshcore: not a discovery response") )
Discovery errors.
var ( ErrBadMAC = errors.New("meshcore: MAC verification failed") ErrBadCiphertext = errors.New("meshcore: ciphertext is not block-aligned") )
ErrBadMAC reports a payload whose MAC does not match its ciphertext.
var ( ErrBadKeyLength = errors.New("meshcore: bad key length") ErrKeyMismatch = errors.New("meshcore: public key does not match private key") ErrBadPublicKey = errors.New("meshcore: invalid public key point") )
Identity errors.
var ( ErrShortFrame = errors.New("meshcore: frame too short") ErrInvalidPathLen = errors.New("meshcore: invalid path_len encoding") ErrEmptyPayload = errors.New("meshcore: empty payload") ErrPayloadTooLarge = errors.New("meshcore: payload exceeds MaxPacketPayload") ErrPathTooLarge = errors.New("meshcore: path exceeds encoded length") )
Wire-format errors returned by Packet parsing and encoding.
var ( ErrPayloadFull = errors.New("meshcore: encoded payload exceeds MaxPacketPayload") ErrBadPayloadType = errors.New("meshcore: payload type not valid for this builder") // ErrBadHashLength reports a dest/src/channel hash that is not // PathHashSize bytes; the reference always writes exactly that. ErrBadHashLength = errors.New("meshcore: hash is not PathHashSize bytes") // ErrNoAck says a text subtype is owed no acknowledgement — not a // failure, the answer to "does this one earn an ack". ErrNoAck = errors.New("meshcore: this text subtype expects no ack") )
ErrPayloadFull reports encrypted content that would overflow a packet.
var ( // ErrRegionBadName — the name is empty or holds a character the // grammar excludes. (The reference lets an empty name through and // creates a nameless region; refusing it is a deliberate // robustness divergence.) ErrRegionBadName = errors.New("meshcore: region name has illegal characters") // ErrRegionBadParent — the parent is the region itself, or one of // its own descendants. (The reference only catches the direct // self-parent; refusing the indirect cycle too is a deliberate // robustness divergence — a cycle would detach a whole subtree // from every export.) ErrRegionBadParent = errors.New("meshcore: region cannot be its own ancestor") // ErrRegionFull — the table already holds MaxRegionEntries. ErrRegionFull = errors.New("meshcore: region table is full") // ErrRegionNotFound — no region has that exact name. ErrRegionNotFound = errors.New("meshcore: region not found") // ErrRegionNotEmpty — the region still has children — and the // wildcard, which is refused with this same verdict, exactly as // the reference answers "Err - not empty" to `region remove *`. ErrRegionNotEmpty = errors.New("meshcore: region has child regions") // ErrRegionRestore — stored region rows do not describe a sound // table (duplicate id, dangling parent, cycle, bad name). ErrRegionRestore = errors.New("meshcore: stored region table is not sound") )
var ( // ErrIsLogin marks an anonymous request that is a password // attempt rather than one of the typed questions. The two share // one byte position, and the reference tells them apart the same // way: a type byte below space is a type, anything else is text. ErrIsLogin = errors.New("meshcore: anonymous request is a login attempt") // ErrBadReplyPath marks a reply path whose descriptor does not // describe the bytes behind it. ErrBadReplyPath = errors.New("meshcore: reply path encoding invalid") // ErrUnknownVersion marks a request whose version byte names a // dialect this library does not speak. ErrUnknownVersion = errors.New("meshcore: request version unknown") // ErrBadKeyPrefix marks a key width outside what a public key has // to give. ErrBadKeyPrefix = errors.New("meshcore: invalid public key prefix width") )
var ErrBadPathEncoding = errors.New("meshcore: bad path encoding in PATH payload")
ErrBadPathEncoding reports a PATH payload whose decrypted path descriptor is invalid.
var ErrBadSignature = errors.New("meshcore: advert signature invalid")
ErrBadSignature reports an advert whose signature does not verify.
var ErrShortAdminFrame = errors.New("meshcore: admin frame shorter than its timestamp prefix")
ErrShortAdminFrame reports a plaintext too short to hold the mandatory timestamp prefix.
var ErrUnknownKeyFormat = errors.New("meshcore: unrecognised private key format")
ErrUnknownKeyFormat reports bytes that match no known private-key serialization.
Functions ¶
func AckCRC ¶
AckCRC computes the 4-byte ACK a recipient must return for a received text message: SHA-256(message ‖ senderPubKey) truncated to 4 bytes, where message is the decrypted datagram content (timestamp, attempt, text). The sender precomputes the same value keyed on its own public key to correlate the reply.
Reference: MeshCore BaseChatMesh composeMsgPacket / onMessageRecv.
func AnonPassword ¶ added in v1.3.0
AnonPassword reads the password out of a login attempt: the text after the timestamp, up to its terminator.
func BuildGroupData ¶ added in v1.10.0
BuildGroupData builds the inner GRP_DATA plaintext: data type, byte length, then the opaque application bytes. Seal it with BuildGroupDatagram.
func BuildGroupText ¶ added in v1.1.0
BuildGroupText builds a GRP_TXT plaintext: timestamp, a zero flags byte (plain is the only group subtype the reference accepts), then "sender: text". Seal it with BuildGroupDatagram(PayloadTypeGrpTxt, …).
func BuildSignedTextPlaintext ¶ added in v1.12.0
func BuildSignedTextPlaintext(sentAt time.Time, authorPrefix []byte, text string, attempt uint8) ([]byte, error)
BuildSignedTextPlaintext builds the plaintext of a signed-plain text — the subtype a room server pushes posts in: the post's timestamp, the flags byte carrying the subtype and two attempt bits, the author's four-byte key prefix, then the text with no terminator (the cipher's padding ends it). The attempt bits are the reference's random two: a retry must hash differently from the push it repeats, and so must the ACK it expects, so the caller draws them fresh per attempt. Seal it with BuildDatagram(PayloadTypeTxtMsg, …); ParseTextPlaintext reads it back with the prefix in SignedPrefix.
Reference: simple_room_server pushPostToClient.
func BuildTextAckBody ¶ added in v1.10.0
BuildTextAckBody creates the ACK payload returned for one decrypted text plaintext. Plain text carries its CRC, extended-attempt byte and one random uniqueness byte; signed text carries only its CRC; both CLI subtypes are owed no body at all. ackPubKey is the public key the reference hashes for this subtype: the sender's key for plain text and the receiver's key for signed text.
A subtype owed nothing answers a nil body and a nil error. Its packet-returning sibling, BuildCommandAck, answers ErrNoAck for the same question: a nil slice is a value a caller can carry, a nil packet is not.
Reference: BaseChatMesh onMessageRecv and onSignedMessageRecv.
func BuildTextPlaintext ¶ added in v1.1.0
BuildTextPlaintext builds the plaintext of a plain or CLI text message: timestamp, a flags byte carrying the subtype, then the text. Seal it with BuildDatagram(PayloadTypeTxtMsg, …).
func BuildTextPlaintextAttempt ¶ added in v1.1.0
BuildTextPlaintextAttempt is BuildTextPlaintext with a retransmission counter. Attempts 0-3 ride in the low two bits of the flags byte; beyond that the byte wraps, so a [0x00][attempt] tail is appended to keep each retransmission's packet hash distinct — matching the reference composeMsgPacket. The expected ACK (see AckCRC) is computed over the timestamp‖flags‖text, NOT the tail, so recompute it per attempt.
func CString ¶ added in v1.3.0
CString reads a wire string up to its terminator — the form every name, password and list crosses the air in.
func Decrypt ¶
Decrypt reverses Encrypt. The secret must be SharedSecretSize bytes and the ciphertext length a multiple of CipherBlockSize; the plaintext keeps any zero padding, exactly as the reference hands padded plaintext to its payload parsers.
func EncodeSNR ¶ added in v1.3.0
EncodeSNR packs a signal-to-noise reading into the reference's quarter-dB byte: the unit every SNR crosses the air in, from a discovery answer to a trace's walked path. Values beyond the byte's reach are clamped rather than wrapped — a radio reporting nonsense should read as the worst or best the wire can say, not as its opposite.
func Encrypt ¶
Encrypt enciphers plaintext with AES-128 in ECB mode — each 16-byte block independently — keyed by the first CipherKeySize bytes of the shared secret, zero-padding the final partial block. The block mode, key truncation and padding are the reference protocol's choices (MeshCore src/Utils.cpp), reproduced here for interoperability. The secret must be SharedSecretSize bytes; the returned length is a multiple of CipherBlockSize.
func EncryptThenMAC ¶
EncryptThenMAC enciphers plaintext, then prepends a CipherMACSize authentication tag: HMAC-SHA256 over the ciphertext, keyed with the FULL SharedSecretSize secret (the cipher uses only its first half), truncated. Output layout: MAC ‖ ciphertext. The secret must be SharedSecretSize bytes.
func FrameAccessList ¶ added in v1.12.0
func FrameAccessList(entries []AccessEntry) []byte
FrameAccessList packs as many rows as one response body carries, and no more: a reply that promised rows it could not seal would never be sent at all. The reference lists admins alone; which rows to offer is the caller's policy, this only lays them out.
func FrameAccessListRequest ¶ added in v1.12.0
func FrameAccessListRequest() []byte
FrameAccessListRequest builds the body an admin asks for the list with: the type and two reserved bytes the reference insists are zero.
func FrameAdmin ¶ added in v1.1.0
FrameAdmin prefixes body with the 4-byte timestamp every admin request and response carries.
func FrameAnonReply ¶ added in v1.3.0
FrameAnonReply builds what every anonymous answer says under the tag: this node's clock, for an easy sync and for packet-hash uniqueness, then whatever the question asked for. A clock question's answer is the clock alone. The tag itself belongs to the frame around this — BuildResponse and FrameAdmin supply it.
func FrameAnonRequest ¶ added in v1.3.0
FrameAnonRequest builds the plaintext of a typed anonymous question. The reply path is the route the answer should take home; an empty one asks for a zero-hop answer.
func FrameKeepAliveAck ¶ added in v1.12.0
FrameKeepAliveAck builds the payload of the ACK a room answers a keep-alive with: the four CRC bytes, then how many posts the client has not yet received — the one ACK on the mesh that carries a fifth byte. Wrap it with BuildAck.
func FrameKeepAliveRequest ¶ added in v1.9.0
FrameKeepAliveRequest builds the authenticated keep-alive body. The room synchronisation cursor is present for every contact; non-room peers simply receive zero, matching the reference companion.
func FrameLoginReply ¶ added in v1.3.0
func FrameLoginReply(r LoginReply) ([]byte, error)
FrameLoginReply builds it, filling the uniqueness blob.
func FrameNeighbours ¶ added in v1.3.0
func FrameNeighbours(total int, entries []NeighbourEntry) []byte
FrameNeighbours packs as many entries as fit, and reports the count it actually wrote rather than the count it was given — a reply that claimed more rows than it carried would strand its reader.
func FrameNeighboursQuery ¶ added in v1.3.0
func FrameNeighboursQuery(q NeighboursQuery) ([]byte, error)
FrameNeighboursQuery builds the request body — the version byte the reference reserves, then the parameters, then a blob so two identical queries do not hash alike.
func FrameStatusRequest ¶ added in v1.9.0
FrameStatusRequest builds the reference's status request body, including the otherwise reserved bytes and uniqueness tail emitted by BaseChatMesh.
func FrameTelemetryRequest ¶ added in v1.9.0
FrameTelemetryRequest builds the reference's telemetry request body and fills its four-byte uniqueness tail from the system CSPRNG.
func KeepAliveAckCRC ¶ added in v1.12.0
KeepAliveAckCRC is the acknowledgement both ends compute for one keep-alive: AckCRC over the request's first nine plaintext bytes — tag, type and cursor, the cursor read as zeros when the request was too short to carry one — and the CLIENT's public key. plain is the whole decrypted request, from its tag; the client computes the same value over what it sent to recognise the answer.
Reference: simple_room_server onPeerDataRecv and BaseChatMesh checkConnections, which both hash nine bytes and the client key.
func MACThenDecrypt ¶
MACThenDecrypt verifies the leading MAC and, if valid, deciphers the remaining bytes. It returns ErrBadMAC when the tag does not match — which, with a 2-byte tag, is also the routine "not for this key" signal the reference uses to probe candidate peers.
func MakeHeader ¶
func MakeHeader(route RouteType, ptype PayloadType, ver PayloadVersion) uint8
MakeHeader packs a route, payload type and payload version into the single-byte packet header.
func MatchScope ¶ added in v1.2.0
func MatchScope(p *Packet, keys []TransportKey) int
MatchScope returns the index of the first key whose Code matches the packet, or -1. A repeater keeps its scope table's keys in one slice and maps the returned index back to its own scope metadata — deciding forwarding without touching any crypto.
func ParseAccessListRequest ¶ added in v1.12.0
ParseAccessListRequest checks one on the server side. The reference answers nothing to a request whose reserved bytes are set — they are where a future query parameter would go, and a server that does not speak that dialect must not guess at it.
func ParseAck ¶
ParseAck extracts the 4-byte CRC of an ACK payload, little-endian — the value AckCRC computes on the sending side.
func ParseKeepAliveAck ¶ added in v1.12.0
ParseKeepAliveAck reads one on the client side. A plain four-byte ACK — a repeater's answer, or an older room's — reports no count.
func ParseKeepAliveRequest ¶ added in v1.12.0
ParseKeepAliveRequest reads the cursor out of a keep-alive body — the bytes after the admin tag. Zero, whether the client sent zero or nothing at all, means "leave my cursor where it is"; a room applies any other value as the client's word on what it already holds.
func ParseMultiAck ¶ added in v1.12.0
ParseMultiAck reads what BuildMultiAck wrote: the CRC of the ACK inside and how many copies are still to come. A MULTIPART carrying anything but an ACK is refused as the wrong payload, not as a short one.
func ParsePrivateKey ¶
func ParsePrivateKey(key []byte) (*LocalIdentity, KeyFormat, error)
ParsePrivateKey loads a private key in any of the ecosystem's serializations, detecting which one it got:
- 32 bytes: a seed;
- 64 bytes whose second half is the public key derived from the first half taken as a seed: seed ‖ pub (Go/libsodium);
- 64 bytes otherwise: the firmware's expanded format.
The two 64-byte layouts are distinguished by that derivation check — a collision would require the expanded key's prefix half to equal a derived public key, which random halves hit with probability 2^-256.
func PathReturnBodyBudget ¶ added in v1.5.0
PathReturnBodyBudget is the longest body BuildPathReturn can carry beside a walked path of pathBytes bytes — the reply to a question that arrived flooded, which pays for the path it came by. It is bounded by both the packet and the reference's own combined-path ceiling, and never reports more than the smaller.
func PubKeyPrefixMatcher ¶
PubKeyPrefixMatcher builds a MineIdentity predicate from a hex prefix, e.g. "f00d" or "ca7" — an odd nibble count matches the high nibble of the trailing byte. Prefixes forcing a reserved first byte (00 or ff) are refused: the firmware would not import such a key.
func ResponseBodyBudget ¶ added in v1.5.0
func ResponseBodyBudget() int
ResponseBodyBudget is the longest body BuildResponse can carry: the sealed budget behind two hashes, less the admin frame's timestamp.
func ScopeNames ¶ added in v1.3.0
ScopeNames splits the comma-joined list a scopes answer carries. The wildcard '*' comes first when the node relays plain floods, and names arrive with their '#' already stripped.
func SealedBudget ¶ added in v1.5.0
SealedBudget reports the longest plaintext EncryptThenMAC can seal behind an envelope of envelopeLen bytes and still fit one packet: the MAC and the cipher's block rounding are both accounted for.
It exists so a caller can size its content before composing it. The alternative is discovering ErrPayloadFull after the fact, which for a node answering a question means a reply that is simply never sent — and the arithmetic reproduced outside this package, where it drifts.
func TextAckPreimage ¶ added in v1.11.0
TextAckPreimage returns the bytes an ACK hashes for one decrypted text: the content up to the end of the text, and no further. The cipher pads what it seals, and the sender hashed what it wrote, not what the padding made of it — so an ACK computed over the whole plaintext correlates with nothing.
Reference: BaseChatMesh onMessageRecv, which hashes 5 + strlen(text) bytes, and the signed variant that skips its four-byte prefix first.
func UnframeAdmin ¶ added in v1.1.0
UnframeAdmin splits a decrypted admin plaintext into its timestamp and the body after it. The body is returned as-is, which for the reference cipher means it may be zero-padded up to a 16-byte block boundary: the plaintext length is not carried on the wire, so the caller interprets the body per command — a password is read up to its first NUL, a typed command by its known length.
func ValidPathLen ¶
ValidPathLen reports whether an encoded path descriptor is well-formed: the reserved 4-byte hash size is refused, and the described path must fit MaxPathSize.
Types ¶
type AccessEntry ¶ added in v1.12.0
type AccessEntry struct {
PubKeyPrefix [AccessKeyPrefixSize]byte
Permissions uint8
}
AccessEntry is one row of a server's access list.
func ParseAccessList ¶ added in v1.12.0
func ParseAccessList(body []byte) []AccessEntry
ParseAccessList reads the rows out of a reply body. The cipher pads the body with zeros, and the wire carries no count, so the list ends at the first row that is all zeros — a key prefix of six zero bytes with the guest role, which no server persists — or at the last whole row.
type AdvTypeFilter ¶ added in v1.1.0
type AdvTypeFilter uint8
AdvTypeFilter selects which node types answer a discovery request: bit N set means advert type N should respond (see AdvType* constants).
func RepeaterFilter ¶ added in v1.1.0
func RepeaterFilter() AdvTypeFilter
RepeaterFilter matches only repeaters — the common discovery target.
func RoomFilter ¶ added in v1.12.0
func RoomFilter() AdvTypeFilter
RoomFilter matches only room servers.
func (AdvTypeFilter) Includes ¶ added in v1.1.0
func (f AdvTypeFilter) Includes(advType uint8) bool
Includes reports whether nodes of advType should answer.
type Advert ¶
type Advert struct {
Identity Identity
Timestamp time.Time
Data *AdvertData
}
Advert is a verified, decoded ADVERT.
func ParseAdvert ¶
ParseAdvert decodes an ADVERT payload and verifies its signature. It returns ErrBadSignature if verification fails; the packet is otherwise structurally valid.
type AdvertData ¶
type AdvertData struct {
Type uint8
HasLoc bool
LatE6 int32 // degrees × 1e6
LonE6 int32
Feat1 uint16 // present when non-zero
Feat2 uint16
Name string
}
AdvertData is the decoded application payload of an ADVERT: node type, optional location (stored as integer millionths of a degree, the wire unit), two optional feature words and an optional name.
func ParseAdvertData ¶
func ParseAdvertData(b []byte) (*AdvertData, error)
ParseAdvertData decodes advert app data. The name, when flagged, is the entire remainder of the buffer.
func (*AdvertData) Dump ¶
func (a *AdvertData) Dump() string
Dump renders a framed view of the app data with its encoded bytes.
func (*AdvertData) EncodeAppData ¶
func (a *AdvertData) EncodeAppData() []byte
EncodeAppData serialises the advert app data. Field order on the wire is fixed: flag byte, then lat/lon, feat1, feat2, name — each present only when its flag is set. A feature word is emitted only when non-zero, matching the reference builder. The name is truncated to the room remaining without splitting a UTF-8 sequence; if no valid prefix fits (e.g. the name starts with a malformed byte), the name and its flag are omitted entirely, as the reference does.
func (*AdvertData) String ¶
func (a *AdvertData) String() string
String renders the app data on one line.
type AnonDatagram ¶
AnonDatagram is the envelope of an ANON_REQ payload: dest hash ‖ sender public key ‖ MAC ‖ ciphertext. The receiver derives the shared secret from SenderPub.
func ParseAnonDatagram ¶
func ParseAnonDatagram(payload []byte) (*AnonDatagram, error)
ParseAnonDatagram splits an ANON_REQ payload.
func (*AnonDatagram) Dump ¶
func (d *AnonDatagram) Dump() string
Dump renders a framed view with a hex dump of the sealed data.
func (*AnonDatagram) Open ¶
func (d *AnonDatagram) Open(secret []byte) ([]byte, error)
Open authenticates and decrypts the sealed data.
func (*AnonDatagram) String ¶
func (d *AnonDatagram) String() string
String renders the envelope on one line.
type AnonReply ¶ added in v1.3.0
AnonReply is a decoded anonymous answer: the answering node's clock, and the text the question asked for — empty for a clock question.
func ParseAnonReply ¶ added in v1.3.0
ParseAnonReply decodes what FrameAnonReply built, from the body UnframeAdmin hands back. The text stops at its terminator: the block cipher pads past it, and reading to the end would hand the caller the padding.
type AnonRequest ¶ added in v1.3.0
type AnonRequest struct {
Timestamp uint32
Kind uint8
// PathLen is the reference's path descriptor — hop count in the
// low six bits, hash width in the top two — and Path the bytes it
// describes. A supplied path of zero hops is not the same as no
// path: it names the asker as adjacent.
PathLen uint8
Path []byte
}
AnonRequest is the decrypted content of a typed anonymous question: when it was asked, what it asks, and the route home it supplies.
func ParseAnonRequest ¶ added in v1.3.0
func ParseAnonRequest(plain []byte) (*AnonRequest, error)
ParseAnonRequest decodes one. A password attempt returns ErrIsLogin, which is a verdict about the request rather than a fault in it.
type Datagram ¶
Datagram is the addressed envelope of a PATH, REQ, RESPONSE or TXT_MSG payload: dest hash ‖ src hash ‖ MAC ‖ ciphertext. Splitting is separate from opening so a receiver can match DestHash (and look up src candidates) before spending a decrypt attempt per shared secret, as the reference does.
func ParseDatagram ¶
ParseDatagram splits an addressed datagram payload.
type DiscoverReq ¶ added in v1.1.0
type DiscoverReq struct {
// Filter selects the responding node types.
Filter AdvTypeFilter
// Tag is an arbitrary value echoed in every response, so a caller
// can match answers to the request that prompted them.
Tag uint32
// Since, when non-zero, asks only nodes whose discovery state
// changed at or after this UNIX time to respond.
Since uint32
// PrefixOnly requests an 8-byte key prefix instead of the full
// public key in each response — smaller replies, coarser identity.
PrefixOnly bool
}
DiscoverReq is a node discovery request.
func ParseDiscoverReq ¶ added in v1.1.0
func ParseDiscoverReq(p *Packet) (*DiscoverReq, error)
ParseDiscoverReq decodes a direct, zero-hop DISCOVER_REQ, for a node deciding whether and how to answer.
type DiscoverResp ¶ added in v1.1.0
type DiscoverResp struct {
// NodeType is the responder's advert type (AdvTypeRepeater, …).
NodeType uint8
// SNR is the signal-to-noise ratio, in dB, at which the responder
// heard the request — a direct measure of the inbound link.
SNR float64
// Tag echoes the request's tag.
Tag uint32
// PubKey identifies the responder: the full 32-byte key, or an
// 8-byte prefix when the request asked for one.
PubKey []byte
}
DiscoverResp is a node's answer to a discovery request.
func ParseDiscoverResp ¶ added in v1.1.0
func ParseDiscoverResp(p *Packet) (*DiscoverResp, error)
ParseDiscoverResp decodes a direct, zero-hop DISCOVER_RESP.
type GroupChannel ¶
type GroupChannel struct {
Hash []byte // PathHashSize bytes
// Secret is always 32 bytes on the crypto path: the reference
// stores channel secrets in a zero-filled 32-byte array, so a
// 16-byte PSK effectively keys the MAC as PSK ‖ 16 zero bytes
// (and the cipher as its first 16 bytes). The channel hash, by
// contrast, is computed over the PSK's real length.
Secret []byte
}
GroupChannel is a shared-key channel. The hash is the leading byte(s) of SHA-256(secret) and prefixes every group packet so receivers can pick candidate channels; the secret keys the payload cipher and MAC.
func NewGroupChannel ¶
func NewGroupChannel(psk []byte) (*GroupChannel, error)
NewGroupChannel derives a channel from its PSK — 16 bytes (the common companion-app channels) or 32. The hash is SHA-256 over the PSK's real length truncated to PathHashSize; the crypto secret is the PSK zero-padded to 32 bytes, both exactly as the reference (BaseChatMesh addChannel / Mesh createGroupDatagram).
func NewGroupChannelFromBase64 ¶
func NewGroupChannelFromBase64(pskBase64 string) (*GroupChannel, error)
NewGroupChannelFromBase64 derives a channel from a base64-encoded PSK (16 or 32 raw bytes) — the form in which secret channels are shared between companion apps.
func NewHashtagChannel ¶
func NewHashtagChannel(tag string) *GroupChannel
NewHashtagChannel derives a public hashtag channel from its tag, as the MeshCore companion apps do: the PSK is SHA-256("#"+tag)[:16]. This is a client convention, not part of the on-wire protocol (MeshCore's own C++ keys every channel from an explicit PSK) — it is reproduced here because it is what "joining #test by name" means in the apps, and it is verified against live traffic in the corpus test. A leading '#' in tag is accepted and not doubled; the tag is used verbatim otherwise (the apps do not fold case).
func NewPublicChannel ¶
func NewPublicChannel() *GroupChannel
NewPublicChannel returns the well-known "Public" channel.
func (GroupChannel) GoString ¶
func (ch GroupChannel) GoString() string
GoString redacts %#v the same way String redacts %v.
func (GroupChannel) String ¶
func (ch GroupChannel) String() string
String identifies the channel by its hash WITHOUT revealing the secret — %v and %+v on a GroupChannel are safe in logs.
type GroupDataPlaintext ¶ added in v1.10.0
GroupDataPlaintext is the decrypted content of GRP_DATA.
func ParseGroupData ¶ added in v1.10.0
func ParseGroupData(plain []byte) (*GroupDataPlaintext, error)
ParseGroupData decodes an inner GRP_DATA plaintext and refuses a declared length that the decrypted bytes do not contain.
type GroupDatagram ¶
GroupDatagram is the envelope of a GRP_TXT or GRP_DATA payload: channel hash ‖ MAC ‖ ciphertext.
func ParseGroupDatagram ¶
func ParseGroupDatagram(payload []byte) (*GroupDatagram, error)
ParseGroupDatagram splits a group datagram payload.
func (*GroupDatagram) Dump ¶
func (d *GroupDatagram) Dump() string
Dump renders a framed view with a hex dump of the sealed data.
func (*GroupDatagram) Open ¶
func (d *GroupDatagram) Open(ch *GroupChannel) ([]byte, error)
Open authenticates and decrypts the sealed data with the channel secret.
func (*GroupDatagram) String ¶
func (d *GroupDatagram) String() string
String renders the envelope on one line.
type GroupTextPlaintext ¶ added in v1.1.0
GroupTextPlaintext is the decrypted content of a GRP_TXT — a channel message whose text is prefixed with the sender's name.
func ParseGroupText ¶ added in v1.1.0
func ParseGroupText(plain []byte) (*GroupTextPlaintext, error)
ParseGroupText decodes a GRP_TXT plaintext, splitting the "sender: text" body. A missing separator leaves Sender empty and the whole body as Text.
type Identity ¶
type Identity struct {
PubKey [PubKeySize]byte
}
Identity is a party in the mesh whose signatures can be verified: an Ed25519 public key. A node's hash, as used in packet paths and payload envelopes, is simply a prefix of this key.
Reference: MeshCore src/Identity.{h,cpp}.
func IdentityFromBytes ¶
IdentityFromBytes builds an Identity from a 32-byte public key.
func (Identity) Hash ¶
Hash returns the first n bytes of the public key — the node hash the mesh uses to address and dedup. n must be in [0, PubKeySize]; it panics otherwise, like any out-of-range slice.
func (Identity) HashMatches ¶
HashMatches reports whether the given hash bytes are a prefix of the public key.
type KeyFormat ¶
type KeyFormat int
KeyFormat identifies one of the private-key serializations found in the MeshCore ecosystem. The expansion seed → expanded is one-way (SHA-512 + clamp): a seed can always be converted to the firmware format, an expanded key can never be turned back into a seed.
const ( // KeyFormatSeed is a bare 32-byte Ed25519 seed — what openHop // (PyNaCl), the Go standard library and libsodium generate from. KeyFormatSeed KeyFormat = iota + 1 // KeyFormatExpanded is the 64-byte orlp/ed25519 layout the MeshCore // firmware stores and exports (prv.key): clamped scalar ‖ signing // prefix, the two halves of SHA-512(seed). KeyFormatExpanded // KeyFormatSeedPub is the 64-byte seed ‖ public-key layout of Go's // ed25519.PrivateKey and libsodium's crypto_sign secret key. KeyFormatSeedPub )
type LPPAccelValue ¶ added in v1.1.0
type LPPAccelValue struct{ X, Y, Z float64 }
LPPAccelValue is acceleration in g on three axes.
type LPPColourValue ¶ added in v1.1.0
type LPPColourValue struct{ R, G, B byte }
LPPColourValue is an 8-bit RGB triple.
type LPPCoordinate ¶ added in v1.1.0
type LPPCoordinate struct{ Latitude, Longitude float64 }
LPPCoordinate is one polyline vertex, in degrees.
type LPPEncoder ¶ added in v1.1.0
type LPPEncoder struct {
// contains filtered or unexported fields
}
LPPEncoder builds a Cayenne LPP buffer one reading at a time. It is not safe for concurrent use; the zero value is ready to use.
func NewLPPEncoder ¶ added in v1.1.0
func NewLPPEncoder() *LPPEncoder
NewLPPEncoder returns an empty encoder.
func NewLPPEncoderWithin ¶ added in v1.6.0
func NewLPPEncoderWithin(limit int) *LPPEncoder
NewLPPEncoderWithin returns an encoder that holds at most limit bytes of whole records. Add answers ErrLPPFull for a reading that would not fit; the buffer stays valid, so a producer may treat the refusal as the normal end of its list. This is how a telemetry answer is sized to the packet that will carry it, before it is composed rather than after it is refused.
func (*LPPEncoder) Add ¶ added in v1.1.0
func (e *LPPEncoder) Add(r LPPReading) error
Add appends one reading, applying the type's scaling (the inverse of LPPDecode). Value must be a float64 for scalar types, or the matching LPP*Value struct for compound ones. Polyline encoding is not yet supported. Floats are rounded to the nearest wire unit.
func (*LPPEncoder) Bytes ¶ added in v1.1.0
func (e *LPPEncoder) Bytes() []byte
Bytes returns the encoded buffer, valid until the next Add or Reset.
func (*LPPEncoder) Reset ¶ added in v1.1.0
func (e *LPPEncoder) Reset()
Reset empties the encoder for reuse.
type LPPGPSValue ¶ added in v1.1.0
type LPPGPSValue struct{ Latitude, Longitude, Altitude float64 }
LPPGPSValue is latitude/longitude in degrees, altitude in metres.
type LPPGyroValue ¶ added in v1.1.0
type LPPGyroValue struct{ X, Y, Z float64 }
LPPGyroValue is angular rate in °/s on three axes.
type LPPPolylineValue ¶ added in v1.1.0
type LPPPolylineValue struct {
Factor byte
Coordinates []LPPCoordinate
}
LPPPolylineValue is a decoded polyline: its factor byte and vertices.
type LPPReading ¶ added in v1.1.0
LPPReading is one decoded record. Value's concrete type follows Type: float64 for scalar types, or one of the LPP*Value structs.
func LPPDecode ¶ added in v1.1.0
func LPPDecode(data []byte) ([]LPPReading, error)
LPPDecode decodes a Cayenne LPP buffer into readings. Channel 0 marks the end of data; trailing bytes after it are ignored.
type LocalIdentity ¶
type LocalIdentity struct {
Identity
// contains filtered or unexported fields
}
LocalIdentity is an identity whose private key is held locally.
The private key uses the reference firmware's expanded layout (orlp/ed25519): bytes 0-31 hold the clamped scalar, bytes 32-63 the signing prefix — both halves of SHA-512(seed). This is NOT the Go standard library layout (seed ‖ public key); identities exported from firmware or companion apps load directly with LocalIdentityFromKeys. The String/GoString redaction methods use value receivers on purpose — pointer receivers would leave fmt of a dereferenced copy printing the raw struct, private key included.
func LocalIdentityFromKeys ¶
func LocalIdentityFromKeys(prv, pub []byte) (*LocalIdentity, error)
LocalIdentityFromKeys loads an expanded 64-byte private key (and, optionally, its 32-byte public key; pass nil to re-derive it, as the reference does when restoring a private key alone). The scalar half must carry the Ed25519 clamp: every genuinely expanded key does, and rejecting the rest loudly beats silently deriving a public key the firmware (which multiplies the raw bytes) would disagree on.
func LocalIdentityFromSeed ¶
func LocalIdentityFromSeed(seed []byte) (*LocalIdentity, error)
LocalIdentityFromSeed expands a 32-byte seed exactly as the reference does: SHA-512, clamp the lower half into the scalar, keep the upper half as the signing prefix.
func MineIdentity ¶
MineIdentity generates random identities until match(pub) returns true, fanning out over all CPUs. Only firmware-importable keys are considered (see FirmwareImportable). It returns the found identity and the number of candidates tried; cancel ctx to bound the search.
Cost scales as 16^nibbles: a 4-hex-char prefix takes tens of thousands of attempts, 6 chars millions.
func NewLocalIdentity ¶
func NewLocalIdentity(rand io.Reader) (*LocalIdentity, error)
NewLocalIdentity generates a fresh identity from the given entropy source (typically crypto/rand.Reader). Public keys starting 0x00 or 0xFF are redrawn: the firmware reserves those prefixes and refuses to import such keypairs, and openHop regenerates the same way.
func (*LocalIdentity) FirmwareImportable ¶
func (li *LocalIdentity) FirmwareImportable() bool
FirmwareImportable reports whether the firmware would accept this identity's keypair: validatePrivateKey refuses public keys starting 0x00 or 0xFF (reserved prefixes).
func (LocalIdentity) GoString ¶
func (li LocalIdentity) GoString() string
GoString redacts %#v the same way String redacts %v.
func (*LocalIdentity) PrvKey ¶
func (li *LocalIdentity) PrvKey() []byte
PrvKey returns a copy of the expanded 64-byte private key.
func (*LocalIdentity) Seed ¶
func (li *LocalIdentity) Seed() []byte
Seed returns the 32-byte seed when this identity was built from one (generated, or loaded from a seed-bearing format), and nil for identities loaded from an expanded key: the expansion is one-way, so firmware-exported keys have no recoverable seed.
func (*LocalIdentity) SharedSecret ¶
func (li *LocalIdentity) SharedSecret(otherPub []byte) ([]byte, error)
SharedSecret performs the reference ECDH: the peer's Ed25519 public key is transposed to its X25519 (Montgomery) form, then multiplied by this identity's clamped scalar. Both parties derive the same 32-byte secret, which keys the payload cipher and MAC.
func (*LocalIdentity) Sign ¶
func (li *LocalIdentity) Sign(message []byte) []byte
Sign produces an Ed25519 signature over message with the expanded private key: r = SHA-512(prefix ‖ M), R = rB, k = SHA-512(R ‖ A ‖ M), S = k·a + r. Signatures are identical to the standard construction for keys expanded from a seed.
func (*LocalIdentity) StdPrivateKey ¶
func (li *LocalIdentity) StdPrivateKey() (ed25519.PrivateKey, bool)
StdPrivateKey returns the identity in Go's ed25519.PrivateKey layout (seed ‖ pub). It reports false when the seed is unknown — an expanded firmware key cannot be represented in seed-bearing formats.
func (LocalIdentity) String ¶
func (li LocalIdentity) String() string
String identifies the local identity WITHOUT revealing any private material — %v and %+v on a LocalIdentity are safe in logs.
type LoginReply ¶ added in v1.3.0
type LoginReply struct {
Clock uint32
// Result is LoginOK on success; the reference defines no others.
Result uint8
// KeepAlive is the reference's legacy hint, in units of sixteen
// seconds, and ships zero.
KeepAlive uint8
IsAdmin bool
Permissions uint8
FirmwareLevel uint8
}
LoginReply is what a server sends back when a login succeeds: its clock, the verdict, the role and permissions granted, and the reply level it speaks at. The random blob exists so two logins never hash alike, and is not read by anyone.
func ParseLoginReply ¶ added in v1.3.0
func ParseLoginReply(body []byte) (*LoginReply, error)
ParseLoginReply decodes one, for a client reading its own answer.
type Multipart ¶
type Multipart struct {
Remaining uint8
Inner PayloadType
Data []byte
}
Multipart is a decoded MULTIPART wrapper: the prefix byte packs the remaining-parts count (high nibble) and the inner payload type (low nibble); Data is the inner payload.
func ParseMultipart ¶
ParseMultipart splits a MULTIPART payload.
type NeighbourEntry ¶ added in v1.3.0
NeighbourEntry is one row of the answer: as much of the node's key as was asked for, how long ago it was heard, and at what signal.
type NeighboursQuery ¶ added in v1.3.0
NeighboursQuery is what a client asks for: how many rows, from where in the list, in what order, and how much of each key it wants.
func ParseNeighboursQuery ¶ added in v1.3.0
func ParseNeighboursQuery(args []byte) (*NeighboursQuery, error)
ParseNeighboursQuery decodes one. Version zero is the only one defined; anything else is a question this library cannot answer.
type NeighboursReply ¶ added in v1.3.0
type NeighboursReply struct {
Total int
Entries []NeighbourEntry
}
NeighboursReply is the answer: how many the node knows, and the rows that fit.
func ParseNeighbours ¶ added in v1.3.0
func ParseNeighbours(body []byte, prefixLen int) (*NeighboursReply, error)
ParseNeighbours decodes the answer. The key width is not on the wire — the asker chose it — so the caller supplies what it asked for.
type Packet ¶
type Packet struct {
// Header packs the route type, payload type and payload version.
// Use MakeHeader and the accessors rather than raw bit twiddling.
Header uint8
// PathLen is the encoded path descriptor, not a byte count:
// bits 0-5 hold the hash count, bits 6-7 hold (hash size - 1).
// A hash-size code of 3 (4-byte hashes) is reserved and invalid.
PathLen uint8
// TransportCodes are carried only by TRANSPORT_* routes and are
// zero otherwise.
TransportCodes [2]uint16
// Path semantics depend on the route type: it accumulates node
// hashes on FLOOD routes, is consumed hop by hop on DIRECT routes,
// is empty on zero-hop packets, and collects one SNR byte per hop
// on TRACE packets (whose intended route rides in the payload).
Path []byte
Payload []byte
}
Packet is the fundamental transmission unit.
Wire layout: header (1 byte) | transport codes (2 × uint16 little-endian, only for TRANSPORT_* routes) | path_len (1 byte) | path (hash count × hash size bytes) | payload (all remaining bytes).
Transport codes travel little-endian: the reference firmware memcpy()s its uint16 fields on little-endian MCUs, which makes LE the de facto wire order.
Reference: MeshCore src/Packet.{h,cpp}.
func BuildAck ¶
BuildAck creates an ACK carrying the given CRC bytes (usually 4, or 6 for the extended form).
func BuildAdvert ¶
func BuildAdvert(id *LocalIdentity, emittedAt time.Time, app *AdvertData) (*Packet, error)
BuildAdvert creates a signed ADVERT packet from this identity. The packet's route defaults to FLOOD; the send layer picks the final route (clearing PH_ROUTE_MASK before setting DIRECT or zero-hop). emittedAt is the advert timestamp (UNIX seconds).
func BuildAnonDatagram ¶
BuildAnonDatagram creates an ANON_REQ: dest hash ‖ sender pub_key ‖ MAC ‖ ciphertext. The sender's full public key travels in the clear so the recipient can derive the shared secret without a prior contact.
func BuildCommandAck ¶ added in v1.11.0
BuildCommandAck creates the acknowledgement a repeater owes one decrypted command line. plain is the whole decrypted text and senderPubKey the key its sender correlates on; the preimage is the content up to the end of the text, never the cipher's padding, which the sender did not hash either.
A subtype owed nothing answers ErrNoAck — the ordinary answer, not a failure. BuildTextAckBody says the same with a nil body and no error, because a nil slice is a value a caller can carry onward and a nil packet is not.
Only the legacy plain subtype earns one. The reference sends its ack before it decides whether the line is a repeat, so a client retrying a command it saw no answer to still learns the line arrived; CLI data and the explicit command subtype carry their own correlation and are answered by the reply alone.
Reference: MeshCore simple_repeater onPeerDataRecv, the TXT_TYPE_PLAIN branch that calls createAck(ack_hash).
func BuildControl ¶
BuildControl creates a CONTROL/discovery packet from opaque bytes.
func BuildDatagram ¶
func BuildDatagram(ptype PayloadType, destHash, srcHash, secret, data []byte) (*Packet, error)
BuildDatagram creates an addressed, encrypted packet (TXT_MSG, REQ or RESPONSE). Payload layout: dest hash ‖ src hash ‖ MAC ‖ ciphertext, where the hashes are PathHashSize prefixes and the ciphertext is EncryptThenMAC(secret, data). The route defaults to FLOOD; the send layer picks the final route before transmitting.
func BuildDiscoverReq ¶ added in v1.1.0
func BuildDiscoverReq(req DiscoverReq) (*Packet, error)
BuildDiscoverReq builds a DISCOVER_REQ. It is direct-routed with an empty path — zero hop — so repeaters answer it but never relay it.
func BuildDiscoverResp ¶ added in v1.1.0
func BuildDiscoverResp(resp DiscoverResp, prefixOnly bool) (*Packet, error)
BuildDiscoverResp builds a node's DISCOVER_RESP. It is zero hop, like the request. prefixOnly must match what the request asked for.
func BuildGroupDatagram ¶
func BuildGroupDatagram(ptype PayloadType, ch *GroupChannel, data []byte) (*Packet, error)
BuildGroupDatagram creates a GRP_TXT or GRP_DATA packet: channel hash ‖ MAC ‖ ciphertext, keyed by the channel secret.
func BuildLoginReq ¶ added in v1.1.0
func BuildLoginReq(id *LocalIdentity, repeaterPub []byte, timestamp uint32, password string) (*Packet, []byte, error)
BuildLoginReq builds an ANON_REQ login to a repeater. The shared secret is derived from this identity and the repeater's public key and returned, because the caller needs it to open the response. The plaintext is the timestamp followed by the password (which may be empty). The packet floods by default; set the route to direct for a zero-hop login to a neighbour.
func BuildMultiAck ¶
BuildMultiAck wraps an ACK as one of a redundant set: a prefix byte (remaining count in the high nibble, ACK type in the low nibble) followed by the CRC bytes.
func BuildPathReturn ¶
func BuildPathReturn( destHash, srcHash, secret []byte, pathLen uint8, path []byte, extraType uint8, extra []byte, ) (*Packet, error)
BuildPathReturn creates a PATH packet returning an observed path to its sender: dest hash ‖ src hash ‖ EncryptThenMAC(path_len ‖ path ‖ extra). When extra is non-empty it is prefixed by extraType; when empty, the reference appends a dummy type byte (0xFF) and 4 random bytes so the packet hash stays unique — this does the same, from crypto/rand. The route defaults to FLOOD; the send layer picks the final route before transmitting.
func BuildRawCustom ¶
BuildRawCustom creates a RAW_CUSTOM packet from opaque bytes.
func BuildRequest ¶ added in v1.1.0
func BuildRequest(id *LocalIdentity, destPub, secret []byte, timestamp uint32, body []byte) (*Packet, error)
BuildRequest builds a REQ command datagram to a known contact, sealed with the shared secret established at login. body is the command; its first byte is a ReqType or an application-defined command.
func BuildResponse ¶ added in v1.1.0
func BuildResponse(destHash, srcHash, secret []byte, timestamp uint32, body []byte) (*Packet, error)
BuildResponse builds a RESPONSE datagram back to a contact, sealed with the same shared secret. timestamp is the responder's clock, or the request's timestamp echoed as a tag.
func BuildRoomLoginReq ¶ added in v1.9.0
func BuildRoomLoginReq(id *LocalIdentity, roomPub []byte, timestamp, syncSince uint32, password string, ) (*Packet, []byte, error)
BuildRoomLoginReq builds the room variant of a companion login. Rooms put the client's last signed-message timestamp ahead of the password so they can resume synchronisation without replaying the entire history.
func BuildTextAck ¶ added in v1.4.0
BuildTextAck creates the ACK a text message expects: AckCRC over the message, laid out the way the wire carries it. message is the decrypted datagram content up to the end of the text — the cipher's padding is not part of it, since the sender never hashed that either — and senderPubKey is the key the sender correlates on.
Reference: MeshCore simple_repeater, the legacy TXT_TYPE_PLAIN ack.
func BuildTrace ¶
BuildTrace creates a TRACE packet: tag (uint32 LE) ‖ auth code (uint32 LE) ‖ flags. The route to trace is appended to the payload by the send layer, and each relay appends its SNR byte to the path.
func ParsePacket ¶
ParsePacket decodes a wire frame into a new Packet.
func (*Packet) AppendPathHash ¶
AppendPathHash appends one node hash to the path — the flood-relay step (Mesh::routeRecvPacket). The hash must be exactly PathHashSize bytes; the grown path must still fit MaxPathSize and the 6-bit count field (63 — one below MaxPathSize for 1-byte hashes, where the reference's length check alone would let the count wrap to zero).
func (*Packet) AppendTo ¶
AppendTo appends the wire form of the packet to dst and returns the extended slice. It validates the same invariants UnmarshalBinary enforces, so every encoded packet round-trips.
func (*Packet) AppendTraceHop ¶ added in v1.3.0
AppendTraceHop records this node's reading of a trace and walks it on. A TRACE path carries one raw quarter-dB byte per hop rather than node hashes, so the descriptor counts bytes and its size bits must read one (Mesh::onRecvPacket).
func (*Packet) ConsumeNextHop ¶
ConsumeNextHop removes and returns the first hash of a direct path — the step a repeater takes after matching it against its own hash (Mesh::removeSelfFromPath).
func (*Packet) Dump ¶
Dump renders a framed, multi-line view of the packet with a full hex dump of its wire form.
func (*Packet) HasTransportCodes ¶
HasTransportCodes reports whether the wire form carries the 4-byte transport code block.
func (*Packet) Hash ¶
func (p *Packet) Hash() [MaxHashSize]byte
Hash returns the packet hash used for deduplication and ACK correlation: SHA-256 over the payload type, then — for TRACE packets only — the path descriptor, then the payload, truncated to MaxHashSize bytes.
The TRACE descriptor is hashed as two little-endian bytes: the reference hashes its uint16 path_len field whole, so the second byte is always zero on the wire but still part of the preimage. TRACE packets fold the descriptor in because they may legitimately revisit a node on their return path; every other type must hash identically however long its accumulated path has grown.
func (*Packet) InheritRouting ¶ added in v1.3.0
InheritRouting makes p travel the way src travelled: same route type, same transport codes, same path already walked. The payload type and version stay p's own. This is the step a relay takes when it rebuilds a packet mid-flight rather than forwarding it verbatim — dropping the transport codes there would strand the rebuilt packet outside the mesh that asked for it.
func (*Packet) IsRouteDirect ¶
IsRouteDirect reports whether the packet follows a supplied path.
func (*Packet) IsRouteFlood ¶
IsRouteFlood reports whether the packet floods (plain or transport).
func (*Packet) MarshalBinary ¶
MarshalBinary implements encoding.BinaryMarshaler.
func (*Packet) PathByteLen ¶
PathByteLen returns the number of path bytes on the wire.
func (*Packet) PathHashCount ¶
PathHashCount returns the number of hashes recorded in the path.
func (*Packet) PathHashSize ¶
PathHashSize returns the per-hop hash width in bytes (1-3; 4 is reserved).
func (*Packet) PayloadType ¶
func (p *Packet) PayloadType() PayloadType
PayloadType returns the packet's payload discriminator.
func (*Packet) PayloadVer ¶
func (p *Packet) PayloadVer() PayloadVersion
PayloadVer returns the packet's payload version.
func (*Packet) RawHex ¶
RawHex returns the wire form as a lowercase hex string, or an error note when the packet cannot encode.
func (*Packet) SetPathHashCount ¶
SetPathHashCount updates the hash count, preserving the hash size.
func (*Packet) SetPathHashSizeAndCount ¶
SetPathHashSizeAndCount sets both halves of the path descriptor.
func (*Packet) String ¶
String renders the packet on one line for trace logs: payload type, route, version, path shape, payload size and dedup hash.
func (*Packet) Summary ¶
Summary is the minimal form: payload type, wire size and the first bytes of the raw frame.
func (*Packet) UnmarshalBinary ¶
UnmarshalBinary implements encoding.BinaryUnmarshaler. It mirrors the reference parser: a frame must carry at least one payload byte, the path descriptor must be well-formed, and the payload must fit MaxPacketPayload. Path and Payload are copies of the input.
func (*Packet) Unscope ¶ added in v1.2.0
func (p *Packet) Unscope()
Unscope returns a packet to plain routing, dropping both codes.
func (*Packet) UnwrapMultipart ¶ added in v1.3.0
UnwrapMultipart opens a MULTIPART and returns both halves of what a relay needs: the parsed wrapper, and a copy of the packet carrying the inner payload alone. Header, path and transport codes are left untouched on that copy — the payload type still reads MULTIPART — because that is the shape the reference hashes for duplicate detection and walks forward (Mesh::forwardMultipartDirect).
type PathReturn ¶
type PathReturn struct {
PathLen uint8 // encoded descriptor, as in Packet.PathLen
Path []byte
ExtraType uint8 // low nibble only; upper bits are reserved
Extra []byte
}
PathReturn is the decrypted content of a PATH payload: the sender's observed path back, plus an optional piggybacked payload. Extra may carry trailing zero padding from the block cipher — the reference receiver passes it on padded, so ExtraType's owner must tolerate it.
func DecodePathReturn ¶
func DecodePathReturn(plain []byte) (*PathReturn, error)
DecodePathReturn interprets an opened (decrypted) PATH datagram, as the reference receiver does: descriptor byte, path bytes, then an extra-type byte and the padded remainder.
It implements the firmware 1.17+ receiver, which rejects a reserved path descriptor (ErrBadPathEncoding); receivers before 1.17 lacked that guard and read straight through such a descriptor. This is the one path-return behavior that varies by firmware version.
func (*PathReturn) Dump ¶
func (pr *PathReturn) Dump() string
Dump renders a framed view with a hex dump of the extra bytes.
func (*PathReturn) String ¶
func (pr *PathReturn) String() string
String renders the decoded path return on one line.
type PayloadType ¶
type PayloadType uint8
PayloadType is the 4-bit payload discriminator carried in a packet header. Reference: MeshCore src/Packet.h.
const ( PayloadTypeReq PayloadType = 0x00 // request (dest/src hashes, MAC) PayloadTypeResponse PayloadType = 0x01 // response to REQ or ANON_REQ PayloadTypeTxtMsg PayloadType = 0x02 // plain text message PayloadTypeAck PayloadType = 0x03 // acknowledgement (CRC of the acked packet) PayloadTypeAdvert PayloadType = 0x04 // node advertising its identity PayloadTypeGrpTxt PayloadType = 0x05 // unverified group text message PayloadTypeGrpData PayloadType = 0x06 // unverified group datagram PayloadTypeAnonReq PayloadType = 0x07 // request with an ephemeral public key PayloadTypePath PayloadType = 0x08 // returned path PayloadTypeTrace PayloadType = 0x09 // path trace collecting per-hop SNR PayloadTypeMultipart PayloadType = 0x0A // one packet of a set PayloadTypeControl PayloadType = 0x0B // control/discovery PayloadTypeRawCustom PayloadType = 0x0F // application-defined raw bytes )
Payload type discriminators, as defined by the reference firmware.
func (PayloadType) String ¶
func (t PayloadType) String() string
type PayloadVersion ¶
type PayloadVersion uint8
PayloadVersion is the 2-bit payload version carried in a packet header.
const ( // PayloadVer1 uses 1-byte src/dest hashes and 2-byte MACs. PayloadVer1 PayloadVersion = 0x00 PayloadVer2 PayloadVersion = 0x01 // reserved PayloadVer3 PayloadVersion = 0x02 // reserved PayloadVer4 PayloadVersion = 0x03 // reserved )
Payload versions; only version 1 is defined by the reference.
type Region ¶ added in v1.7.0
type Region struct {
ID uint16
Parent uint16
Flags RegionFlags
Name string
}
Region is one entry: RegionEntry{id, parent, flags, name[31]}. The name is stored as given — a '#' or '$' prefix included — truncated to what the reference's buffer holds. ID 0 is the wildcard and never appears in the table. Flags is exported mutable on purpose: allowf/denyf are, in the reference, a direct write to the entry.
func (*Region) BareName ¶ added in v1.7.0
BareName is the display and CSV form: the name without its '#' (skip_hash — a '$' prefix stays, as upstream).
func (*Region) IsWildcard ¶ added in v1.7.0
IsWildcard reports whether this is the root region.
type RegionDefError ¶ added in v1.7.0
type RegionDefError struct{ Reason string }
RegionDefError is a refusal from the def DSL. Reason is the exact text the reference appends after "Err - ", so a CLI can reproduce its replies verbatim.
func (*RegionDefError) Error ¶ added in v1.7.0
func (e *RegionDefError) Error() string
type RegionFlags ¶ added in v1.7.0
type RegionFlags uint8
RegionFlags is a region's policy bits.
const ( // RegionDenyFlood excludes the region from flood forwarding. RegionDenyFlood RegionFlags = 0x01 // RegionDenyDirect is reserved upstream and never set. RegionDenyDirect RegionFlags = 0x02 )
type RegionLoader ¶ added in v1.7.0
type RegionLoader struct {
// contains filtered or unexported fields
}
RegionLoader stages a `region load`: lines accumulate into a fresh table, and nothing touches the live map until Commit. The staging keeps the source's next id, so an id freed mid-load is not reissued to a different name (resetFrom).
func (*RegionLoader) Commit ¶ added in v1.7.0
func (l *RegionLoader) Commit() *RegionMap
Commit returns the staged table as the new map. Divergence, deliberate: the home and default designations follow their region BY NAME into the new table when it still holds one — the reference loses both on every load. A designation whose name did not survive resets (home to the wildcard, default to none). The caller swaps the returned map in and re-derives whatever it cached.
func (*RegionLoader) Line ¶ added in v1.7.0
func (l *RegionLoader) Line(line string)
Line feeds one dump line: indent in single spaces (the level), a name cut at the first byte outside the name grammar, and an optional 'F' anywhere after it for flood allowed. A region already in the SOURCE map carries its id and flags over, F ignored. Lines at indent 0 (the dump's own '*' line), deeper than the stack, with no name, or under a level never seen are dropped in silence — byte-faithful to the reference, which answers nothing either way.
One repair, deliberate: the dump glues '^' onto the home region's name, and '^' is itself a legal name byte — read literally, a dump with a home set corrupts the one region it marks into "name^" on reload. A single trailing '^' is therefore stripped; the mark is a designation, not a name, and Commit re-attaches the designation by name.
type RegionMap ¶ added in v1.7.0
type RegionMap struct {
// contains filtered or unexported fields
}
RegionMap holds the table, the wildcard, and the node's designations. Insertion order is load-bearing: every export and every match walks it, so two nodes loaded from the same dump render the same tree.
func NewRegionMap ¶ added in v1.7.0
func NewRegionMap() *RegionMap
NewRegionMap is an empty table: just the wildcard, flags clear, next id 1, no home, no default.
func RestoreRegionMap ¶ added in v1.7.0
func RestoreRegionMap(entries []Region, wildcardFlags RegionFlags, nextID, homeID, defaultID uint16, ) (*RegionMap, error)
RestoreRegionMap rebuilds a map from persisted rows, in their stored order. It applies the same next-id repair the reference's file loader does, and — beyond the reference — refuses a table that is not sound: duplicate or zero ids, bad names, a parent that names no entry, or a cycle.
func (*RegionMap) ApplyDef ¶ added in v1.7.0
ApplyDef runs the `region def` DSL: space-separated segments, a cursor starting at the wildcard. `name` creates (or re-parents) under the cursor and descends into it; `name|jump` or `name,jump` creates, then moves the cursor to the region the jump prefix resolves to. Every touched region has its flags cleared. There is no rollback: a refusal mid-way leaves the earlier segments applied, exactly as the reference does; the caller renders the tree (or the error) afterwards.
func (*RegionMap) Default ¶ added in v1.7.0
Default is the region the node's own traffic is scoped to, nil when none is designated (or the designation dangles).
func (*RegionMap) DefaultID ¶ added in v1.7.0
DefaultID exposes the default designation for persistence; 0 means none.
func (*RegionMap) Entries ¶ added in v1.7.0
Entries copies the table in insertion order — the persistence and snapshot view. Mutations go through the live pointers the finders return.
func (*RegionMap) ExportNames ¶ added in v1.7.0
func (m *RegionMap) ExportNames(mask RegionFlags, invert bool, budget int) string
ExportNames renders the CSV `region list` and the anonymous regions answer use: '*' first when the wildcard passes the filter, then the bare names in insertion order. mask selects on flags — clear bits pass, invert flips the test. An entry that would overflow max is skipped, not truncated, and the walk continues: the reference keeps looking for shorter names that still fit.
func (*RegionMap) ExportTree ¶ added in v1.7.0
ExportTree renders the hierarchy the way `region` prints it: depth in single spaces, the bare name, '^' glued on the home region (the wildcard wears it while no home is set), and " F" on every region whose flood is allowed. Pre-order, children in insertion order.
func (*RegionMap) FindByName ¶ added in v1.7.0
FindByName finds by exact name; "*" names the wildcard, and a '#' is ignored on both sides of the comparison.
func (*RegionMap) FindByPrefix ¶ added in v1.7.0
FindByPrefix resolves the abbreviated form every verb except `remove` accepts: an exact match wins outright, otherwise the LAST entry the prefix matches — both quirks of the reference that a companion app may have learned to rely on.
func (*RegionMap) FindMatch ¶ added in v1.7.0
func (m *RegionMap) FindMatch(p *Packet, mask RegionFlags) *Region
FindMatch is the forwarding decision: the first entry, in insertion order, whose mask bits are clear and whose key's code the packet carries. The wildcard is not consulted — an unscoped packet is the wildcard's business, and it never carries a code. The hierarchy plays no part: the match is flat, as it is in the reference.
func (*RegionMap) Home ¶ added in v1.7.0
Home is the designated home region. Id 0 designates the wildcard; a designation left dangling by a removal reports nil, exactly as the reference's getHomeRegion can.
func (*RegionMap) HomeID ¶ added in v1.7.0
HomeID exposes the home designation for persistence; 0 means the wildcard.
func (*RegionMap) KeysFor ¶ added in v1.7.0
func (m *RegionMap) KeysFor(r *Region) []TransportKey
KeysFor derives the transport keys a region listens under: one auto key for a '#' or bare name, none for a '$' private region — the external keystore those use is not implemented here, matching the rest of this library.
func (*RegionMap) Put ¶ added in v1.7.0
Put creates a region under parentID, or — when the name already exists — re-parents the existing entry, keeping its id and flags. A new entry starts with flood denied, as the reference's putRegion does; every CLI path then clears the flags itself. The parent must not be the region or any of its descendants.
func (*RegionMap) Remove ¶ added in v1.7.0
Remove deletes a region by its exact name. A region with children refuses — children go first — and so does the wildcard, with the same verdict the reference gives it. A dangling home or default designation is left as the reference leaves it: the getters report it honestly.
func (*RegionMap) SetDefault ¶ added in v1.7.0
SetDefault designates the default region; nil clears it.
func (*RegionMap) SetHome ¶ added in v1.7.0
SetHome designates the home region; nil resets to the wildcard.
func (*RegionMap) StartLoad ¶ added in v1.7.0
func (m *RegionMap) StartLoad() *RegionLoader
StartLoad opens a staging table against this map. Divergence, deliberate: the wildcard's flags carry into the staging — the reference silently resets them to allow-all on commit.
type RepeaterStats ¶ added in v1.3.0
type RepeaterStats struct {
BattMilliVolts uint16
TxQueueLen uint16
NoiseFloor int16
LastRSSI int16
PacketsRecv uint32
PacketsSent uint32
TxAirtimeSecs uint32
UptimeSecs uint32
SentFlood uint32
SentDirect uint32
RecvFlood uint32
RecvDirect uint32
ErrEvents uint16
// LastSNR is carried in quarter dB, as EncodeSNR packs it, but
// over two bytes rather than one.
LastSNR float64
DirectDups uint16
FloodDups uint16
RxAirtimeSecs uint32
RecvErrors uint32
}
RepeaterStats is a repeater's own tally, the reference's struct field for field. Every count is cumulative since the node came up.
func ParseRepeaterStats ¶ added in v1.3.0
func ParseRepeaterStats(b []byte) (*RepeaterStats, error)
ParseRepeaterStats decodes them, for a client reading another node's status.
func (RepeaterStats) AppendTo ¶ added in v1.3.0
func (s RepeaterStats) AppendTo(b []byte) []byte
AppendTo packs the statistics in the reference's order, which is the order its C struct lays out in memory — every field aligned to its own width, no padding needed.
type RoomLogin ¶ added in v1.12.0
RoomLogin is the decrypted content of a room server's ANON_REQ: the client's clock, the room-clock timestamp of the newest post it already holds, and the password it offers. A blank password is a returning member asking to be recognised by its key alone. The room variant differs from a repeater's login by exactly the cursor, which is why AnonPassword, applied to one, reads four cursor bytes as the start of the word.
func ParseRoomLogin ¶ added in v1.12.0
ParseRoomLogin decodes what BuildRoomLoginReq sealed, once opened. The password stops at its terminator: the cipher pads past it.
Reference: simple_room_server onAnonDataRecv.
type RoomStats ¶ added in v1.12.0
type RoomStats struct {
BattMilliVolts uint16
TxQueueLen uint16
NoiseFloor int16
LastRSSI int16
PacketsRecv uint32
PacketsSent uint32
TxAirtimeSecs uint32
UptimeSecs uint32
SentFlood uint32
SentDirect uint32
RecvFlood uint32
RecvDirect uint32
ErrEvents uint16
// LastSNR is carried in quarter dB over two bytes, as the
// repeater's is.
LastSNR float64
DirectDups uint16
FloodDups uint16
// Posted counts posts stored since the room came up, PostPushes
// the deliveries attempted — retries included.
Posted uint16
PostPushes uint16
}
RoomStats is a room server's own tally, the reference's ServerStats field for field. The first forty-eight bytes are the repeater's (RepeaterStats up to its duplicate counts); where the repeater goes on to receive airtime and errors, the room counts posts.
func ParseRoomStats ¶ added in v1.12.0
ParseRoomStats decodes them, for a client reading a room's status.
type RouteType ¶
type RouteType uint8
RouteType is the 2-bit routing mode carried in a packet header. Reference: MeshCore src/Packet.h.
const ( // RouteTransportFlood floods and carries transport codes. RouteTransportFlood RouteType = 0x00 // RouteFlood floods; the path accumulates one node hash per hop. RouteFlood RouteType = 0x01 // RouteDirect follows a supplied path, consumed one hop at a time. RouteDirect RouteType = 0x02 // RouteTransportDirect is direct routing with transport codes. RouteTransportDirect RouteType = 0x03 )
type TextPlaintext ¶ added in v1.1.0
type TextPlaintext struct {
Timestamp time.Time
Type uint8 // one of TxtType*
Attempt uint8 // retransmission counter
// SignedPrefix is the sender's four-byte synchronisation prefix on
// signed-plain messages. It is empty for every other subtype.
SignedPrefix []byte
Text string
}
TextPlaintext is the decrypted content of a TXT_MSG (the plaintext behind a peer Datagram).
func ParseTextPlaintext ¶ added in v1.1.0
func ParseTextPlaintext(plain []byte) (*TextPlaintext, error)
ParseTextPlaintext decodes a TXT_MSG plaintext. The body may be zero- padded by the cipher block, so the text ends at its first NUL; a non-zero byte past that NUL is the extended retransmission counter.
type Trace ¶
type Trace struct {
Tag uint32
AuthCode uint32
Flags uint8
HashWidth int // 1 << (Flags & 0x03), per firmware v1.11+
Route []byte
SNRx4 []int8
}
Trace is a decoded TRACE packet. The requested route rides in the payload (RouteHashes, node-hash entries of HashWidth bytes each); the traversed hops record their SNR in the packet path, in quarter dB units (SNRx4, one signed byte per hop).
func ParseTrace ¶
ParseTrace decodes a TRACE packet (payload and path together).
A TRACE's own path is not made of node hashes: it carries one raw quarter-dB byte per hop walked, so its descriptor must declare a width of one. A packet claiming any other width is refused here rather than read as SNR readings it does not hold — the same invariant AppendTraceHop enforces when the path grows, so a caller cannot be told a trace is walkable and then refused as it walks.
type TransportKey ¶
type TransportKey [16]byte
TransportKey is a 16-byte scope key.
func NewTransportKey ¶
func NewTransportKey(raw []byte) (TransportKey, error)
NewTransportKey wraps a raw 16-byte scope key (the keys behind private '$' scopes, held outside this library).
func TransportKeyForName ¶
func TransportKeyForName(name string) TransportKey
TransportKeyForName derives the auto (hashtag) scope key from a scope name: SHA-256("#"+name)[:16], as the reference does for '#' and bare (implicit hashtag) scope names (TransportKeyStore getAutoKeyFor via RegionMap getTransportKeysFor). A leading '#' is accepted and not doubled. Private ('$') scopes use externally stored keys instead — wrap those with NewTransportKey.
func (TransportKey) Code ¶
func (k TransportKey) Code(p *Packet) uint16
Code returns the transport code a packet carries when its sender scopes it to this key: HMAC-SHA256(key, payload_type ‖ payload) truncated to 2 bytes and read little-endian, with 0x0000 and 0xFFFF reserved (bumped to 0x0001 and 0xFFFE — those two values mark the "unscoped" and broadcast cases on the wire). Reference: TransportKey::calcTransportCode.
func (TransportKey) IsZero ¶
func (k TransportKey) IsZero() bool
IsZero reports whether the key is all zero — the "null"/unscoped sentinel the reference uses (TransportKey::isNull).
func (TransportKey) Matches ¶
func (k TransportKey) Matches(p *Packet) bool
Matches reports whether the packet is scoped to this key: it carries transport codes and its transport_codes[0] equals this key's Code. This is the repeater's forwarding-decision primitive. It is false for plain FLOOD/DIRECT packets, which carry no transport codes and are simply unscoped.
func (TransportKey) Scope ¶ added in v1.2.0
func (k TransportKey) Scope(p *Packet)
Scope confines a packet to this scope: it stamps the transport code derived from the packet's own payload and turns the route into its scoped form — a flood becomes TRANSPORT_FLOOD, a direct packet TRANSPORT_DIRECT. A null key leaves the packet untouched and unscoped, which is what the reference does when no default scope is configured (MyMesh::sendFloodScoped).
The second code stays zero. The reference reserves it for the sender's home scope and has never written anything else there; a reader that finds a value in it is reading a protocol this library does not yet speak.
Scope must be called once the payload is final: the code is computed over it, so any later edit invalidates the scope. The path may still change afterwards — a relay appends its hash to a scoped flood and the code stays valid, which is what lets a scope survive every hop.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
meshkey
command
Command meshkey works with MeshCore node keys from the shell.
|
Command meshkey works with MeshCore node keys from the shell. |
|
meshmon
command
Command meshmon subscribes to an MQTT broker where mesh observers publish captured traffic and pretty-prints each MeshCore packet.
|
Command meshmon subscribes to an MQTT broker where mesh observers publish captured traffic and pretty-prints each MeshCore packet. |
|
Package companion implements the wire formats spoken between a MeshCore companion device and its controlling application.
|
Package companion implements the wire formats spoken between a MeshCore companion device and its controlling application. |